为什么在(* x).into()中需要显式取消引用,而在x.my_into()中却不需要?

Fed*_*ico 10 traits dereference implicit-conversion rust

在阅读了方法调用表达式解引用运算符方法查找自动解引用之后,我认为我对该主题有了很好的理解。但是后来我遇到了一种情况,我希望自动重新引用会发生,而实际上却没有发生。

示例如下。

#[derive(Clone, Copy, Debug)]
struct Foo();

impl Into<&'static str> for Foo {
    fn into(self) -> &'static str {
        "<Foo as Into>::into"
    }
}

fn vec_into<F: Copy + Into<T>, T>(slice: &[F]) -> Vec<T> {
    slice.iter().map(|x| (*x).into()).collect()
}

fn main() {
    let array = [Foo(), Foo(), Foo()];
    let vec = vec_into::<_, &'static str>(&array);
    println!("{:?}", vec);
}
Run Code Online (Sandbox Code Playgroud)

上面的代码有效,但是我认为不需要(*x).into()在函数中vec_into进行显式取消引用。我的理由是,因为x: &Foo,然后x.into()就试图找到接受类型的方法&Foo&&Foo&mut &FooFoo&Foo&mut Foo

这是因为存在取消引用链&FooFoo,对于U此链中的每个,我们还插入&U&mut U

我的直觉被以下代码在没有任何显式取消引用的情况下也有效的事实所证实。

#[derive(Clone, Copy, Debug)]
struct Foo();

trait MyInto<T> {
    fn my_into(self) -> T;
}

impl MyInto<&'static str> for Foo {
    fn my_into(self) -> &'static str {
        "<Foo as MyInto>::my_into"
    }
}

fn vec_my_into<F: Copy + MyInto<T>, T>(slice: &[F]) -> Vec<T> {
    slice.iter().map(|x| x.my_into()).collect()
}

fn main() {
    let array = [Foo(), Foo(), Foo()];
    let my_vec = vec_my_into(&array);
    println!("{:?}", my_vec);
}
Run Code Online (Sandbox Code Playgroud)

在这里x: &Foo隐式取消引用以便调用该方法<Foo as MyInto<&'static str>>::my_into

一个较小的例子

鉴于上述定义FooMyInto,代码

let result: &str = (&Foo()).my_into()
Run Code Online (Sandbox Code Playgroud)

可行,但是

let result: &str = (&Foo()).into()
Run Code Online (Sandbox Code Playgroud)

无法编译错误

error[E0277]: the trait bound `&str: std::convert::From<&Foo>` is not satisfied
  --> src/bin/into.rs:34:33
   |
34 |     let result: &str = (&Foo()).into();
   |                                 ^^^^ the trait `std::convert::From<&Foo>` is not implemented for `&str`
   |
   = note: required because of the requirements on the impl of `std::convert::Into<&str>` for `&Foo`
Run Code Online (Sandbox Code Playgroud)

Sve*_*ach 5

Rust 完全按照你的描述执行方法查找,它会立即找到一个候选.into()——全面实现

impl<T, U> Into<U> for T
where
    U: From<T>,
{
    fn into(self) -> U {
        U::from(self)
    }
}
Run Code Online (Sandbox Code Playgroud)

这个实现满足了候选方法的所有要求——它是可见的,在范围内并且为 type 定义&Foo,因为它是为任何type定义的T。一旦编译器选择了这个候选者,它就会注意到 trait boundsU不满足,并发出你看到的错误。

for 的情况MyInto完全不同,因为您没有提供基于From. 如果你这样做,你会得到同样的错误。

可以争论的是,如果不满足特征边界,编译器应该跳过一揽子实现,并继续处理候选类型列表,直到找到更合适的。语言规范在这一点上实际上并不完全清楚,但从错误中我们可以清楚地看到编译器实际做什么。