为什么我得到错误 "the trait `Foo` is not implemented for `&mut T`" 即使 T 实现了特征?
Why do I get the error "the trait `Foo` is not implemented for `&mut T`" even though T implements the trait?
我有这个来源:
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提供二维图形库。总的来说,一定有一些关于特质的东西我误解了。
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>,
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,
{}
另请参阅:
错误消息说“graphics::character::CharacterCache
没有为 &mut C
实现”;实际上,您在 where
子句中所说的只是 C: CharacterCache
、 而不是 &mut C: CharacterCache
。
(一般情况下,只知道Type: Trait
就不能得出&mut Type: Trait
的结论)
我假设您在 self.properties: Text
上调用的 .draw
方法需要一个 &mut C
作为其参数,因此您可以传入 [=23] =] 或 &mut *font
,但我猜你通过 &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 旁注:
这种编码 "mistake"(放入一个额外的间接层)实际上比你在 Rust 编程时想象的要多。
然而,人们通常不会注意到它,因为编译器通常会将预期类型与提供的类型进行比较,并应用所谓的 deref coercions 将给定值转换为合适的值参数。
因此,如果您考虑以下代码:
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
和 &concrete
,在这种情况下)。
(您可以通过阅读相关的 RFC 来了解更多关于 Deref 强制转换的历史。)
但是,当我们调用的函数需要对类型参数的引用时,这种魔法并不能拯救我们,如下所示:
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
) 将分解为满足 T: AddAssign
.
的某种类型 &mut T
它检查第一个可能匹配的情况:剥离第一个 &mut
,然后看剩下的 (&mut i32
) 是否可能是我们的 T
搜索。
&mut i32
未实现 AddAssign
,因此尝试解决特征约束失败。
关键是:编译器 而不是 然后决定尝试在此处应用任何强制转换(包括 deref 强制转换);它只是放弃了。我没有设法找到放弃这里的基础的历史记录,但我从对话(以及编译器的知识)中得到的记忆是特征解析步骤是昂贵的,所以我们选择不尝试搜索潜在的特征对每一步都进行胁迫。相反,程序员应该找出一个适当的转换表达式,将给定类型 T
转换为编译器 可以 接受的某种中间类型 U
预期类型。
我有这个来源:
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提供二维图形库。总的来说,一定有一些关于特质的东西我误解了。
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>,
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,
{}
另请参阅:
错误消息说“graphics::character::CharacterCache
没有为 &mut C
实现”;实际上,您在 where
子句中所说的只是 C: CharacterCache
、 而不是 &mut C: CharacterCache
。
(一般情况下,只知道Type: Trait
就不能得出&mut Type: Trait
的结论)
我假设您在 self.properties: Text
上调用的 .draw
方法需要一个 &mut C
作为其参数,因此您可以传入 [=23] =] 或 &mut *font
,但我猜你通过 &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 旁注:
这种编码 "mistake"(放入一个额外的间接层)实际上比你在 Rust 编程时想象的要多。
然而,人们通常不会注意到它,因为编译器通常会将预期类型与提供的类型进行比较,并应用所谓的 deref coercions 将给定值转换为合适的值参数。
因此,如果您考虑以下代码:
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
和 &concrete
,在这种情况下)。
(您可以通过阅读相关的 RFC 来了解更多关于 Deref 强制转换的历史。)
但是,当我们调用的函数需要对类型参数的引用时,这种魔法并不能拯救我们,如下所示:
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
) 将分解为满足 T: AddAssign
.
&mut T
它检查第一个可能匹配的情况:剥离第一个 &mut
,然后看剩下的 (&mut i32
) 是否可能是我们的 T
搜索。
&mut i32
未实现 AddAssign
,因此尝试解决特征约束失败。
关键是:编译器 而不是 然后决定尝试在此处应用任何强制转换(包括 deref 强制转换);它只是放弃了。我没有设法找到放弃这里的基础的历史记录,但我从对话(以及编译器的知识)中得到的记忆是特征解析步骤是昂贵的,所以我们选择不尝试搜索潜在的特征对每一步都进行胁迫。相反,程序员应该找出一个适当的转换表达式,将给定类型 T
转换为编译器 可以 接受的某种中间类型 U
预期类型。