Rust为原生所有权类型实现Trait为何引用类型可自动生效
问题描述
这或许是个基础问题,但我始终无法理解其中原理:仅为原生所有权类型实现Trait后,为何对应的引用类型无需额外编写impl代码,就能直接获得相同的Trait实现并调用对应方法?
对应的复现代码如下:
trait Cool: Sized + std::fmt::Debug { fn cool(self) { println!("cool -> {:?}", self); } } impl Cool for i32 {} // 取消注释下方代码,程序依然可以正常运行 // impl Cool for &i32 {} fn main(){ let val = 123; val.cool(); (&val).cool(); }
原理解释
这个现象本质是Rust编译器在方法调用场景下的**自动解引用(auto-deref)**匹配规则生效,和「引用类型自动获得原生类型的Trait实现」没有任何关系。
- 当你调用
receiver.method()形式的方法时,Rust编译器会按照固定顺序,反复尝试对接收者做自动引用、自动解引用操作,逐次匹配是否存在对应的方法实现,直到找到匹配项为止。 - 你的示例中
(&val).cool()的实际匹配流程是:- 首先检查
&i32类型是否实现了Cooltrait、存在cool方法,结果是没有; - 编译器自动对
&val做解引用,拿到i32类型,刚好匹配你为i32实现的cool(self)方法; - 由于
i32实现了Copytrait,解引用后会直接复制一份值传入方法作为self参数,不会触发所有权转移错误,因此代码可以正常运行。
- 首先检查
要验证
&i32并没有真的实现Cooltrait很简单:如果你写一个要求泛型参数必须实现Cool的函数,直接传入&val会直接编译失败:fn require_cool<T: Cool>(input: T) {} fn main() { let val = 123; require_cool(&val); // 编译报错:the trait bound `&i32: Cool` is not satisfied }只有手动为
&i32编写impl Cool for &i32 {}之后,上述代码才能通过编译。
如果把示例中的i32换成没有实现Copy的类型(比如String),你会发现(&val).cool()会直接报错——自动解引用后需要把引用背后的值move进方法,但共享引用不允许移动背后的值,这也进一步证明了引用类型并没有自动获得原生类型的Trait实现:
impl Cool for String {} fn main() { let s = String::from("test"); s.cool(); // 正常运行,String所有权被move进方法 (&s).cool(); // 编译报错:cannot move out of `s` which is behind a shared reference }
内容的提问来源于stack exchange,提问作者rsalmei
相关产品推荐
相关产品推荐

