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

2

17

I have this source:

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,
    );
}

And the error

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

The only aspect of C that is defined is that it implements CharacterCache, yet the error says the opposite.

DrawState, Matrix2d, CharacterCache and its implementations, Texture, and self.properties (Text) are provided by the Piston 2d graphics library. There must be something about traits in general that I'm misunderstanding.

The Text::draw function signature:

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>,
Ship answered 5/7, 2017 at 14:21 Comment(0)
B
26

T, &T, and &mut T are all different types; and that means that &mut &mut T is likewise a different type. Traits are not automatically implemented for references to a type. If you wish to implement a trait for either of the references, you need to write it out explicitly.

As an example, this exhibits the same problem:

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
}

Explicit implementations of the trait for the references solve the problem:

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

In many cases, you can delegate the function implementations to the non-reference implementation.

If this should always be true, you can make it so by applying it to all references to a type that implements a trait:

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

If you don't have control over the traits, you may need to specify that you take a reference to a generic type that implements the trait:

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

See also:

Boyse answered 5/7, 2017 at 14:39 Comment(0)
O
7

The error message says that "graphics::character::CharacterCache is not implemented for &mut C"; and indeed, all you have said in your where-clause is that C: CharacterCache, not &mut C: CharacterCache.

(In general, one cannot conclude &mut Type: Trait if all one knows is Type: Trait)

I'm assuming that the .draw method that you are invoking on self.properties: Text wants a &mut C for its argument, so you might be able to pass in either font or &mut *font, but I'm guessing that your extra level of indirection via &mut font is causing a problem there.

In other words:

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

Sidenote for Experienced Rustaceans:

This kind of coding "mistake" (putting in an extra level of indirection) actually occurs more than you might think when programming in Rust.

However, one often does not notice it, because the compiler will often compare the expected type with the type that was provided, and will apply so-called deref coercions to turn in the given value into an appropriate argument.

So if you consider the following code:

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);

it will run without a problem; the compiler will automatically insert dereferences underneath the borrow, turning &mut &mut concrete into &mut *(&mut concrete), and & &concrete into & *(&concrete) (aka &mut concrete and &concrete respectively, in this case).

(You can read more about the history of Deref Coercions by reading the associated RFC.)

However, this magic does not save us when the function we are calling is expecting a reference to a type parameter, like so:

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_);

In this code, the Rust compiler starts off assuming that the input type (&mut &mut i32) will decompose into some type &mut T that satisfies T: AddAssign.

It checks the first case that can possibly match: peel off the first &mut, and then see if the remainder (&mut i32) could possibly be the T that we are searching for.

&mut i32 does not implement AddAssign, so that attempt to solve the trait constraints fails.

Here's the crucial thing: the compiler does not then decide to try applying any coercions here (including deref coercions); it just gives up. I have not managed to find a historical record of the basis for giving up here, but my memory from conversations (and from knowledge of the compiler) is that the trait resolution step is expensive, so we choose not to try to search for potential traits on each step of a coercion. Instead, the programmer is expected to figure out an appropriate conversion expression that will turn the given type T into some intermediate type U that the compiler can accept as the expected type.

Outlive answered 5/7, 2017 at 14:40 Comment(4)
Passing font instead of &mut font is almost guaranteed to be the right solution for this case.Boyse
Yeah I agree. (As an aside, I keep getting to update my answer with a digression about why deref-coercion did not apply in this case, since often one can get away with passing & &X when &X is expected, but I end up concluding that it will only obscure the answer.)Outlive
Could always add an "advanced" or "side note" section at the end if you think it'd be useful to other people in the future.Boyse
@Boyse your comment was the spark, but this entire thread was extremely helpful.Ship

© 2022 - 2024 — McMap. All rights reserved.