【问题标题】:Why do I get the error "the trait `Foo` is not implemented for `&mut T`" even though T implements the trait?为什么即使 T 实现了特征,我也会收到错误“没有为 `&mut T` 实现特征 `Foo`”?
【发布时间】:2018-05-24 10:42:58
【问题描述】:

我有这个来源:

pub fn draw<G, C>(&self, font: &mut C, draw_state: &DrawState, transform: Matrix2d, g: &mut G)
where
    C: CharacterCache,
    G: Graphics<Texture = <C as CharacterCache>::Texture>,
{
    self.properties.draw(
        self.text.as_str(),
        &mut font,
        &draw_state,
        transform,
        g,
    );
}

还有错误

the trait bound `&mut C: graphics::character::CharacterCache` is not satisfied 
(the trait `graphics::character::CharacterCache` is not implemented for `&mut C`) 

C 唯一定义的方面是它实现了CharacterCache,但错误却相反。

DrawStateMatrix2dCharacterCache 及其实现、Texture 和 self.properties (Text) 由 Piston 2d 图形库提供。总的来说,我对特征的理解一定有一些东西。

Text::draw 函数签名:

fn draw<C, G>(
    &self,
    text: &str,
    cache: &mut C,
    draw_state: &DrawState,
    transform: Matrix2d,
    g: &mut G,
) where
    C: CharacterCache,
    G: Graphics<Texture = C::Texture>,

【问题讨论】:

    标签: rust traits associated-types


    【解决方案1】:

    错误消息说“graphics::character::CharacterCache 没有为&amp;mut C 实现”;事实上,你在where-clause 中所说的只是C: CharacterCache不是 &amp;mut C: CharacterCache

    (一般情况下,如果所有人都知道Type: Trait,则无法得出结论&amp;mut Type: Trait

    我假设您在 self.properties: Text 上调用的 .draw 方法需要 &amp;mut C 作为其参数,因此您可以传入 font&amp;mut *font,但我'我猜测您通过&amp;mut font 的额外间接级别会导致那里出现问题。

    换句话说:

    self.properties.draw(
            self.text.as_str(),
            &mut font,
         // ~~~~~~~~~ is not the same as `font` or `&mut *font`
            &draw_state,
            transform,
            g,
        );
    

    给有经验的 Rustaceans 的旁注:

    这种编码“错误”(加入额外的间接层)实际上比您在使用 Rust 编程时可能想象的要多。

    但是,人们通常不会注意到它,因为编译器通常会将预期类型与提供的类型进行比较,并会应用所谓的deref coercions 将给定值转换为适当的参数。

    所以如果你考虑下面的代码:

    fn gimme_ref_to_i32(x: &i32, amt: i32) -> i32 { *x + amt }
    fn gimme_mutref_to_i32(x: &mut i32, amt: i32) { *x += amt; }
    
    let mut concrete = 0;
    gimme_mutref_to_i32(&mut concrete, 1);
    gimme_mutref_to_i32(&mut &mut concrete, 20);
    let i1 = gimme_ref_to_i32(&concrete, 300);
    let i2 = gimme_ref_to_i32(& &concrete, 4000);
    
    println!("concrete: {} i1: {} i2: {}", concrete, i1, i2);
    

    它将毫无问题地运行;编译器会自动在借位下插入取消引用,将&amp;mut &amp;mut concrete 转换为&amp;mut *(&amp;mut concrete),将&amp; &amp;concrete 转换为&amp; *(&amp;concrete)(在本例中分别为&amp;mut concrete&amp;concrete)。

    (您可以通过阅读相关的RFC 了解更多关于 Deref Coercions 的历史。)

    但是,当我们调用的函数期望引用类型参数时,这种魔法并不能拯救我们,如下所示:

    fn gimme_mutref_to_abs<T: AddAssign>(x: &mut T, amt: T) { *x += amt; }
    
    let mut abstract_ = 0;
    gimme_mutref_to_abs(&mut abstract_, 1);
    gimme_mutref_to_abs(&mut &mut abstract_, 1);
    //                  ^^^^ ^^^^^^^^^^^^^^
    // compiler wants   &mut T               where T: AddAssign
    
    println!("abstract: {}", abstract_);
    

    在这段代码中,Rust 编译器开始假设输入类型 (&amp;mut &amp;mut i32) 将分解为满足 T: AddAssign 的某种类型 &amp;mut T

    它检查第一个可能匹配的案例:剥离第一个 &amp;mut,然后查看剩余的 (&amp;mut i32) 是否可能是我们正在搜索的 T

    &amp;mut i32 没有实现AddAssign,因此解决特征约束的尝试失败。

    这是关键:编译器然后决定尝试在此处应用任何强制(包括 deref 强制);它只是放弃了。我没有设法找到放弃这里的依据的历史记录,但我从对话中(以及从编译器的知识)中的记忆是特征解析步骤很昂贵,所以我们选择不尝试搜索潜在的特征在每一步的强制。相反,程序员应该找出一个适当的转换表达式,将给定类型T 转换为编译器可以接受为预期类型的​​某个中间类型U

    【讨论】:

    • 传递 font 而不是 &amp;mut font 几乎可以保证是这种情况下的正确解决方案。
    • 是的,我同意。 (顺便说一句,我不断更新我的答案,题外话为什么 deref-coercion 在这种情况下不适用,因为当&amp;X 是意料之中,但我最终得出结论,它只会掩盖答案。)
    • 如果您认为将来对其他人有用,可以在末尾添加“高级”或“旁注”部分。
    • @Shepmaster 您的评论是火花,但整个线程非常有帮助。
    【解决方案2】:

    T&amp;T&amp;mut T都是不同的类型;这意味着&amp;mut &amp;mut T 同样是不同的类型。对类型的引用不会自动实现特征。如果您希望为任一引用实现特征,则需要明确写出。

    作为一个例子,这表现出同样的问题:

    trait Foo {}
    
    #[derive(Debug, Copy, Clone)]
    struct S;
    impl Foo for S {}
    
    fn example<T>(_: T)
    where
        T: Foo,
    {}
    
    fn main() {
        let mut s = S;
    
        example(s);
        example(&s);     // the trait bound `&S: Foo` is not satisfied
        example(&mut s); // the trait bound `&mut S: Foo` is not satisfied
    }
    

    对引用的 trait 的显式实现解决了问题:

    impl<'a> Foo for &'a S {}
    impl<'a> Foo for &'a mut S {}
    

    在许多情况下,您可以将函数实现委托给非参考实现。

    如果这应该总是正确的,您可以通过将其应用于实现特征的类型的所有引用来实现:

    impl<'a, T> Foo for &'a T where T: Foo {}
    impl<'a, T> Foo for &'a mut T where T: Foo {}
    

    如果您无法控制 trait,您可能需要指定引用实现该 trait 的泛型类型:

    fn example<T>(_: &mut T)
    where
        for<'a> &'a mut T: Foo,
    {}
    

    另见:

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-29
      • 2017-12-05
      • 1970-01-01
      相关资源
      最近更新 更多