为什么在 (*x).into() 中需要显式取消引用,但在 x.my_into() 中不需要?
Why is an explicit dereference required in (*x).into(), but not in x.my_into()?
读完method-call expressions, dereference operator, method lookup, and 后,我觉得我对这个主题有了很好的理解;但后来我遇到了一种情况,我希望自动取消引用发生,但实际上并没有发生。
例子如下
#[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 &Foo
、Foo
、&Foo
, &mut Foo
.
这是因为存在取消引用链 &Foo
→ Foo
,并且对于此链中的每个 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);
}
此处 x: &Foo
被隐式取消引用,以便调用方法 <Foo as MyInto<&'static str>>::my_into
。
一个更小的例子
鉴于Foo
和MyInto
的上述定义,代码
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
。一旦编译器选择了这个候选者,它就会注意到 U
上的特征边界不满足,并发出您看到的错误。
MyInto
的情况完全不同,因为您没有提供基于 From
的全面实施。如果你这样做,你会得到同样的错误。
有人认为,如果不满足特征边界,编译器应该跳过一揽子实现,并继续处理候选类型列表,直到找到更合适的类型。语言规范实际上在这一点上并不完全清楚,但是从我们看到的错误中可以清楚地知道编译器实际上做了什么。
有趣的是,在检查编译器输出后,Into
实现似乎只是调用了特征 From
的方法。它正在寻找的是 std::collections::From<&Foo> for &str
。因此,如果我们实现该特征,编译器确实会找到我们的函数并执行它。
使用以下声明:
#[derive(Clone, Copy, Debug)]
struct Foo();
impl std::convert::From<&Foo> for &str {
fn from(f: &Foo) -> &'static str {
"A &str"// This could be replaced with an actual implementation
}
}
您的代码如您所愿:
let result: &str = (&Foo()).into();// No compiler errors.
您想要的原始代码确实有效,而且实际上并不难实现。
读完method-call expressions, dereference operator, method lookup, and
例子如下
#[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 &Foo
、Foo
、&Foo
, &mut Foo
.
这是因为存在取消引用链 &Foo
→ Foo
,并且对于此链中的每个 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);
}
此处 x: &Foo
被隐式取消引用,以便调用方法 <Foo as MyInto<&'static str>>::my_into
。
一个更小的例子
鉴于Foo
和MyInto
的上述定义,代码
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
。一旦编译器选择了这个候选者,它就会注意到 U
上的特征边界不满足,并发出您看到的错误。
MyInto
的情况完全不同,因为您没有提供基于 From
的全面实施。如果你这样做,你会得到同样的错误。
有人认为,如果不满足特征边界,编译器应该跳过一揽子实现,并继续处理候选类型列表,直到找到更合适的类型。语言规范实际上在这一点上并不完全清楚,但是从我们看到的错误中可以清楚地知道编译器实际上做了什么。
有趣的是,在检查编译器输出后,Into
实现似乎只是调用了特征 From
的方法。它正在寻找的是 std::collections::From<&Foo> for &str
。因此,如果我们实现该特征,编译器确实会找到我们的函数并执行它。
使用以下声明:
#[derive(Clone, Copy, Debug)]
struct Foo();
impl std::convert::From<&Foo> for &str {
fn from(f: &Foo) -> &'static str {
"A &str"// This could be replaced with an actual implementation
}
}
您的代码如您所愿:
let result: &str = (&Foo()).into();// No compiler errors.
您想要的原始代码确实有效,而且实际上并不难实现。