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>,
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:
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,
);
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With