为什么我得到错误 "the trait `Foo` is not implemented for `&mut T`" 即使 T 实现了特征?

Why do I get the error "the trait `Foo` is not implemented for `&mut T`" even though T implements the trait?

我有这个来源:

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提供二维图形库。总的来说,一定有一些关于特质的东西我误解了。

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>,

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
}

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

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 {}

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

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

另请参阅:

错误消息说“graphics::character::CharacterCache 没有为 &mut C 实现”;实际上,您在 where 子句中所说的只是 C: CharacterCache 而不是 &mut C: CharacterCache

(一般情况下,只知道Type: Trait就不能得出&mut Type: Trait的结论)

我假设您在 self.properties: Text 上调用的 .draw 方法需要一个 &mut C 作为其参数,因此您可以传入 [=23] =] 或 &mut *font,但我猜你通过 &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 旁注:

这种编码 "mistake"(放入一个额外的间接层)实际上比你在 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);

它将 运行 没有问题;编译器会自动在借用下面插入取消引用,将 &mut &mut concrete 变成 &mut *(&mut concrete),将 & &concrete 变成 & *(&concrete)(也分别是 &mut concrete&concrete,在这种情况下)。

(您可以通过阅读相关的 RFC 来了解更多关于 Deref 强制转换的历史。)

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

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 编译器开始假设输入类型 (&mut &mut i32) 将分解为满足 T: AddAssign.

的某种类型 &mut T

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

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

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