12

我有这个来源:

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了,但错误却相反。

DrawState, Matrix2d,CharacterCache及其实现, 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>,
4

2 回答 2

19

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

也可以看看:

于 2017-07-05T14:39:27.993 回答
5

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

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

我假设.draw您正在调用的方法需要self.properties: Texta&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,
    );

给有经验的 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);

它将毫无问题地运行;编译器将自动在借用下方插入取消引用,变成&mut &mut concreteinto&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_);

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

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

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

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

于 2017-07-05T14:40:11.253 回答