为什么我会得到错误“特征`Foo`没有为'& mut T`实现”,即使T实现了这个特性?

时间:2017-07-05 14:21:26

标签: rust traits 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,
    );
}

错误

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

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

Piston 2d图形库提供了

DrawStateMatrix2dCharacterCache及其实现,Texture和self.properties(Text)。总的来说,一定有关于特征的东西我误解了。

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

2 个答案:

答案 0 :(得分: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
}

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

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

另见:

答案 1 :(得分:3)

错误消息显示&{34; graphics::character::CharacterCache未针对&mut C&#34;;事实上,你在where - 条款中所说的就是C: CharacterCache不是 &mut C: CharacterCache

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

我假设您在.draw上调用的self.properties: Text方法需要&mut C作为其参数,因此您可以传入{{1} }}或font,但我猜测你通过&mut *font获得额外的间接级别会导致问题。

换句话说:

&mut font

经验丰富的Rustaceans的旁注:

这种编码&#34;错误&#34; (放入额外的间接级别)实际上比在Rust中编程时想象的要多。

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

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

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

它会毫无问题地运行;编译器将自动在借用下插入解引用,将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,在这种情况下)。

(您可以通过阅读相关的RFC来详细了解Deref强制的历史。

然而,当我们调用的函数期望引用类型参数时,这种魔法不能保存我们,如下所示:

&concrete

在此代码中,Rust编译器首先假设输入类型(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_); )将分解为满足&mut &mut i32的某种类型&mut T

检查可能匹配的第一个案例:剥离第一个T: AddAssign,然后查看余数(&mut)是否可能是我们要搜索的&mut i32

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

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