在阅读了method-call expressionsdereference operatormethod lookupauto-dereferencing之后,我认为我对这门学科有了相当好的理解;但后来我遇到了一种情况,在这种情况下,我预期会发生自动解引用,而事实上它并没有发生.

下面是一个例子.

#[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);
}

上面的代码可以工作,但我认为不需要函数vec_into中的显式解引用(*x).into().我的推理是,既然x: &Foo,那么x.into()会试图找到接受类型&Foo&&Foo&mut &FooFoo&Foo&mut Foo的方法.

这是因为存在解引用&Foo的链→ 我们在这条链上分别插入&mut UFoo.

我的直觉得到了以下事实的证实:下面的代码也可以工作,没有任何明确的取消引用.

#[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);
}

这里,x: &Foo被隐式地解引用以调用方法<Foo as MyInto<&'static str>>::my_into.

A smaller example

鉴于上述FooMyInto的定义

let result: &str = (&Foo()).my_into()

有效,但是

let result: &str = (&Foo()).into()

未能编译,但出现错误

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`

推荐答案

正如你所描述的,Rust会精确地执行方法查找,它会立即找到.into()blanket implementation的候选者

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

该实现满足候选方法的所有要求——它是可见的,在范围内,并为类型&Foo定义,因为它是为any类型T定义的.一旦编译器 Select 了这个候选者,它会注意到U上的trait边界不满足,并发出您看到的错误.

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

有人可能会说,如果特征边界不满足,编译器应该跳过整体实现,继续处理候选类型列表,直到找到更好的匹配.语言规范在这一点上实际上并不完全清楚,但从错误中我们可以清楚地看到编译器的实际功能.

Rust相关问答推荐

我如何在Rust中使用传递依赖中的特征?

在特征中使用Async时,如何解决不透明类型`impl Future<;out=self>;`不满足其关联的类型边界和警告?

如何使用syn插入 comments ?

在Rust中宏的表达式中提取对象

在自定义序列化程序中复制serde(With)的行为

如何在函数中返回自定义字符串引用?

将一个泛型类型转换为另一个泛型类型

实现 Deref 的 struct 可以返回对外部数据的引用吗?

如何强制匹配的返回类型为()?

为什么 Rust 需要可变引用的显式生命周期而不是常规引用?

在没有任何同步的情况下以非原子方式更新由宽松原子操作 Select 的值是否安全?

sha256 摘要仅适用于 &*

Button.set_hexpand(false) 不会阻止按钮展开

如何获取模块树?

实现AsyncWrite到hyper Sender时发生生命周期错误

std::vector::shrink_to_fit 如何在 Rust 中工作?

从嵌入式 Rust 中的某个时刻开始经过的时间

隐式类型闭包的错误生命周期推断

Cargo:如何将整个目录或文件包含在功能标志中?

为什么 Bevy 的 Trait 边界不满足 Rapier 物理插件?