Rust Trait 对象转换
Rust Trait object conversion
由于此错误的两个实例,以下代码将无法编译:
error[E0277]: the trait bound Self: std::marker::Sized
is not satisfied
我不明白为什么在这种情况下需要 Sized
,因为 &self
和 &Any
都是指针,并且操作不需要知道结构的大小实现特征,它只需要知道指针本身和它正在转换的类型,因为 &self
在特征内部实现时是通用的。
我认为这可能是编译器强制执行不必要的约束的一个实例,我考虑过向 rust-lang GitHub 存储库提出问题,但我想我应该看看这里是否有人知道我的事情在我提出问题之前不要这样做。
use std::any::Any;
trait Component: Any {
fn as_any(&self) -> &Any {
self
}
fn as_any_mut(&mut self) -> &mut Any {
self
}
}
替代方法是为实现此特征的结构创建 as_any()
和 as_any_mut()
所需函数,但对于这些结构,实现始终与此处向每个人显示的完全一样字符,导致出现多个相同样板代码的实例。
动态大小的类型也可以实现特征。特别是,当您定义 object-safe 特征时,编译器还会定义一个与特征同名的动态大小类型,它允许您使用对象类型,例如 &Component
.
&Component
或 &Any
等对象类型不仅仅是普通的指针;他们是 胖指针 。胖指针结合了一个指向数据的指针和另一段数据:对于对象类型,它是指向 vtable 的指针;对于切片,它是切片的长度。
当从常规指针(例如 &Button
)转换为对象类型时,编译器静态地知道将哪个虚表放入胖指针(例如 Button
的虚表 Any
).另一方面,Rust 不支持从一个对象类型转换为另一个对象类型(例如从 &Component
到 &Any
),因为对象中没有足够的数据来初始化新的胖指针。这就是编译器在错误消息中添加此注释的原因:
= note: required for the cast to the object type `std::any::Any + 'static`
有两种方法可以解决这个问题:
要求所有实现 Component
的类型都是 Sized
:
trait Component: Any + Sized {
fn as_any(&self) -> &Any {
self
}
fn as_any_mut(&mut self) -> &mut Any {
self
}
}
这会导致您根本无法使用 &Component
或 Box<Component>
等对象类型。
使 as_any
和 as_any_mut
方法仅在 Self
为 Sized
时可用:
trait Component: Any {
fn as_any(&self) -> &Any
where Self: Sized
{
self
}
fn as_any_mut(&mut self) -> &mut Any
where Self: Sized
{
self
}
}
这样,您仍然可以为特征使用对象类型,但您将无法对其调用 as_any
和 as_any_mut
。
我发现我认为是不需要新的编译器功能的出色解决方案。
pub trait Component {
// ...
}
pub trait ComponentAny: Component + Any {
fn as_any(&self) -> &Any;
fn as_any_mut(&mut self) -> &mut Any;
}
impl<T> ComponentAny for T
where T: Component + Any
{
fn as_any(&self) -> &Any {
self
}
fn as_any_mut(&mut self) -> &mut Any {
self
}
}
从这里开始,我只是将所有 API 更改为接受 ComponentAny
而不是 Component
。因为 Any
是为任何 'static
类型自动实现的,所以 ComponentAny
现在会为任何实现 Component
的 'static
类型自动实现。感谢 Is there a way to combine multiple traits in order to define a new trait? 的想法。
由于此错误的两个实例,以下代码将无法编译:
error[E0277]: the trait bound
Self: std::marker::Sized
is not satisfied
我不明白为什么在这种情况下需要 Sized
,因为 &self
和 &Any
都是指针,并且操作不需要知道结构的大小实现特征,它只需要知道指针本身和它正在转换的类型,因为 &self
在特征内部实现时是通用的。
我认为这可能是编译器强制执行不必要的约束的一个实例,我考虑过向 rust-lang GitHub 存储库提出问题,但我想我应该看看这里是否有人知道我的事情在我提出问题之前不要这样做。
use std::any::Any;
trait Component: Any {
fn as_any(&self) -> &Any {
self
}
fn as_any_mut(&mut self) -> &mut Any {
self
}
}
替代方法是为实现此特征的结构创建 as_any()
和 as_any_mut()
所需函数,但对于这些结构,实现始终与此处向每个人显示的完全一样字符,导致出现多个相同样板代码的实例。
动态大小的类型也可以实现特征。特别是,当您定义 object-safe 特征时,编译器还会定义一个与特征同名的动态大小类型,它允许您使用对象类型,例如 &Component
.
&Component
或 &Any
等对象类型不仅仅是普通的指针;他们是 胖指针 。胖指针结合了一个指向数据的指针和另一段数据:对于对象类型,它是指向 vtable 的指针;对于切片,它是切片的长度。
当从常规指针(例如 &Button
)转换为对象类型时,编译器静态地知道将哪个虚表放入胖指针(例如 Button
的虚表 Any
).另一方面,Rust 不支持从一个对象类型转换为另一个对象类型(例如从 &Component
到 &Any
),因为对象中没有足够的数据来初始化新的胖指针。这就是编译器在错误消息中添加此注释的原因:
= note: required for the cast to the object type `std::any::Any + 'static`
有两种方法可以解决这个问题:
要求所有实现
Component
的类型都是Sized
:trait Component: Any + Sized { fn as_any(&self) -> &Any { self } fn as_any_mut(&mut self) -> &mut Any { self } }
这会导致您根本无法使用
&Component
或Box<Component>
等对象类型。使
as_any
和as_any_mut
方法仅在Self
为Sized
时可用:trait Component: Any { fn as_any(&self) -> &Any where Self: Sized { self } fn as_any_mut(&mut self) -> &mut Any where Self: Sized { self } }
这样,您仍然可以为特征使用对象类型,但您将无法对其调用
as_any
和as_any_mut
。
我发现我认为是不需要新的编译器功能的出色解决方案。
pub trait Component {
// ...
}
pub trait ComponentAny: Component + Any {
fn as_any(&self) -> &Any;
fn as_any_mut(&mut self) -> &mut Any;
}
impl<T> ComponentAny for T
where T: Component + Any
{
fn as_any(&self) -> &Any {
self
}
fn as_any_mut(&mut self) -> &mut Any {
self
}
}
从这里开始,我只是将所有 API 更改为接受 ComponentAny
而不是 Component
。因为 Any
是为任何 'static
类型自动实现的,所以 ComponentAny
现在会为任何实现 Component
的 'static
类型自动实现。感谢 Is there a way to combine multiple traits in order to define a new trait? 的想法。