为什么我得到错误"特征`Foo`没有为`&mut T`实现",即使T实现了特性?

cov*_*sh2 7 traits rust associated-types

我有这个来源:

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,
    );
}
Run Code Online (Sandbox Code Playgroud)

而错误

the trait bound `&mut C: graphics::character::CharacterCache` is not satisfied 
(the trait `graphics::character::CharacterCache` is not implemented for `&mut C`) 
Run Code Online (Sandbox Code Playgroud)

C定义的唯一方面是它实现CharacterCache,但错误说明相反.

DrawState,Matrix2d,CharacterCache和其实施方式中,Texture和self.properties( Text)由活塞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>,
Run Code Online (Sandbox Code Playgroud)

She*_*ter 9

T,&T&mut T,都是不同的类型; 这意味着它&mut &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
}
Run Code Online (Sandbox Code Playgroud)

引用的特征的显式实现解决了这个问题:

impl<'a> Foo for &'a S {}
impl<'a> Foo for &'a mut S {}
Run Code Online (Sandbox Code Playgroud)

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

如果这应该始终为true,则可以通过将其应用于对实现特征的类型的所有引用来实现:

impl<'a, T> Foo for &'a T where T: Foo {}
impl<'a, T> Foo for &'a mut T where T: Foo {}
Run Code Online (Sandbox Code Playgroud)

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

fn example<T>(_: &mut T)
where
    for<'a> &'a mut T: Foo,
{}
Run Code Online (Sandbox Code Playgroud)

也可以看看:


pnk*_*lix 6

错误消息显示“graphics::character::CharacterCache未实现&mut C”;事实上,您在where- 子句中所说的只是C: CharacterCache而不是 &mut C: CharacterCache

(一般来说,&mut Type: Trait如果只知道的话,就不能得出结论Type: Trait

我假设.draw您调用的方法self.properties: Text需要 a&mut C作为其参数,因此您可能可以传入fontor &mut *font,但我猜测您额外的间接 via 级别&mut font会导致出现问题。

换句话说:

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

给经验丰富的 Rustaceans 的旁注:

这种编码“错误”(引入额外的间接级别)实际上比您在 Rust 中编程时想象的要多。

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

因此,如果您考虑以下代码:

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);
Run Code Online (Sandbox Code Playgroud)

它将毫无问题地运行;编译器将自动在借用下面插入取消引用,将其变为&mut &mut concrete&mut *(&mut concrete)& &concreteinto & *(&concrete)(在本例中分别称为&mut concrete&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_);
Run Code Online (Sandbox Code Playgroud)

在此代码中,Rust 编译器首先假设输入类型 ( ) 将分解为满足 的&mut &mut i32某种类型。&mut TT: AddAssign

它检查第一个可能匹配的情况:剥离第一个&mut,然后查看余数 ( &mut i32) 是否可能是T我们正在搜索的。

&mut i32不实现AddAssign,因此尝试解决特征约束失败。

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