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.
font
instead of&mut font
is almost guaranteed to be the right solution for this case. – Boyse