Rust方法调用的行为在The Rust Reference中进行了描述.它声明"在查找方法调用时,为了调用方法,接收方可能会自动取消引用或borrow ."这一行为将在本章更详细地解释.

在本例中,您可以看到into_char移动Suit,而to_charborrow Suit.

  • into_char,则它由一个值和一个引用调用,并且该引用自动取消引用.

  • to_char,则它也被值和引用调用,并且该值被自动borrow .

对于值和引用Suit都实现了From.因此,当被值调用时,调用Into<Suit>::into(),并且当被引用调用时,调用Into<&Suit>::into().

However, shouldn't I only need to implement one of these traits? When I comment out either implementation the Rust compiler does not compile...

这个过程似乎是这样的.首先生成一个"候选接收器类型"列表.这是通过"反复取消引用",最后"在最后try 一个巨大的胁迫"来实现的.此外,"对于每个候选人T,将&amp;T和&amp;mut T添加到紧接在T之后的列表中."然后,对于这些"候选接收器类型"中的每一个,寻找"直接在T上实现的方法"和"由T实现的可见特征提供的方法".

假设只为char实现了From<Suit>.那么应该对char执行Into<Suit>.

  • 当调用let c: char = value.into();时,"候选接收器类型"应至少包含Suit&Suit&mut Suit.然后,很容易为列表中的第一项解析Into<T>::into().因此,Into<Suit>::into()被称为.

  • 但是,当调用let f: char = reference.into();时,"候选接收器类型"也应该至少包含&Suit&&Suit&mut &Suit*&Suit = Suit&Suit(再次)和&mut Suit.则Into<T>::into()找不到&Suit&&Suit&mut &Suit的实现,但随后找到*&Suit = Suit的实现.因此,Into<Suit>::into()被称为Into<Suit>::into().

Is my logic correct? Why doesn't this work if I comment-out one of the 100 implementations?

Rust Playground

#[derive(Clone, Copy)]
pub enum Suit {
    Club,
    Diamond,
    Heart,
    Spade,
}

pub use Suit::*;

impl Suit {
    #[inline(never)]
    pub fn into_char(self) -> char {
        match self {
            Club => 'C',
            Diamond => 'D',
            Heart => 'H',
            Spade => 'S',
        }
    }
    
    #[inline(never)]
    pub fn to_char(&self) -> char {
        match self {
            Club => 'C',
            Diamond => 'D',
            Heart => 'H',
            Spade => 'S',
        }
    }
}

impl std::convert::From<Suit> for char {
    fn from(suit: Suit) -> Self {
        suit.into_char()
    }
}

impl std::convert::From<&Suit> for char {
    fn from(suit: &Suit) -> Self {
        suit.to_char()
    }
}

fn main() {
    let value = Club;
    let reference = &value;
    
    let a: char = value.into_char();
    let b: char = value.to_char();
    let c: char = value.into();
    println!("{}, {}, {}", a, b, c);
    
    let d: char = reference.into_char();
    let e: char = reference.to_char();
    let f: char = reference.into();
    println!("{}, {}, {}", d, e, f);
}

编辑:正如下面讨论的,我创建了better reproduction%的问题,这确实有助于缩小导致这种行为的原因.

推荐答案

这是因为在匹配特征时,编译器不会考虑后面的推理类型.所以,虽然我们知道into()应该会产生B,但编译器还不知道这一点.所以它所看到的就是我们想要一个Into Suit as Into<_>的实现,用于some类型_.因为它不知道_是什么类型,所以它匹配被编译器视为Into<_> for _impl<U, T: From<U>> Into<T> for U,因为它无法验证where T: From<U>,因为_可以是任何类型.因此,编译器 Select 这个实现(它具有更高的优先级,因为它不使用AutoRef),而不会进一步搜索impl Into<_> for &Suit.

稍后,当编译器返回这个决定时,它意识到这个Iml不再匹配-但它不会返回到搜索auref类型的隐含;它已经完成了这一点.因此,我们最终没有匹配的Impl.

Rust相关问答推荐

在一个tauri协议处理程序中调用一个rectuc函数的推荐技术是什么?

如何在tauri—leptos应用程序中监听后端值的变化?""

关于如何初始化弱 struct 字段的语法问题

在跨平台应用程序中使用std::OS::Linux和std::OS::Windows

你是如何在铁 rust 一侧的金牛座获得应用程序版本的?

有没有办法指定只在Rust的测试中有效的断言?

有没有一种惯用的方法来判断VEC中是否存在变体?

对于已经被认为是未定义行为的相同数据,纯粹存在`&;[u32]`和`&;mut[u32]`吗?

在什么情况下 `..._or()` 比 `..._or_else(|| {})` 更好,为什么?

使用 serde::from_value 反序列化为泛型类型

如何基于常量在Rust中跳过一个测试

为什么Rust编译器会忽略模板参数应具有静态生命周期?

我如何取消转义,在 Rust 中多次转义的字符串?

Rust 中的自动取消引用是如何工作的?

为什么不可变特征的实现可以是可变的?

在 Rust 中使用 `SecTrustSettingsSetTrustSettings` 绑定导致 `errSecInternalComponent`

为什么 &i32 可以与 Rust 中的 &&i32 进行比较?

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

Rust:如果我知道只有一个实例,那么将可变borrow 转换为指针并返回(以安抚borrow 判断器)是否安全?

如何制作具有关联类型的特征的类型擦除版本?