Rust trait 问题 trait 不能变成一个对象 [英] Rust trait issues trait cannot be made into an object
问题描述
我正在尝试编写一些代码来生成具有随机值的随机结构.我有以下结构的特征和帮助宏:
I'm trying to write some code that will generate a random struct with a random value. I have the following trait and helper macros for the structs:
use rand::{thread_rng, Rng};
use std::fmt;
pub trait DataType {
/// generate a new instance of the type with a random value
fn random() -> Box<Self>;
/// generate a new instance of the same type with a random value
fn gen_another(&self) -> Box<Self>;
}
macro_rules! impl_data_type_for_num {
($x:ident) => {
impl DataType for $x {
fn random() -> Box<Self> {
Box::new(Self {
value: thread_rng().gen()
})
}
fn gen_another(&self) -> Box<Self> {
Self::random()
}
}
};
}
macro_rules! impl_formatting {
($x:ident, $s:expr) => {
impl fmt::Debug for $x {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, $s)
}
}
impl fmt::Display for $x {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.value)
}
}
};
}
然后我使用宏在一堆结构上实现所需的特征(例如,这里有几个):
Then I use the macros to implement the needed traits on a bunch of structs (heres a few for example):
pub struct VirtBool {
value: bool
}
impl_data_type_for_num!(VirtBool);
impl_formatting!(VirtBool, "bool");
pub struct VirtU8 {
value: u8
}
impl_data_type_for_num!(VirtU8);
impl_formatting!(VirtU8, "u8");
pub struct VirtU16 {
value: u16
}
impl_data_type_for_num!(VirtU16);
impl_formatting!(VirtU16, "u16");
到目前为止一切正常,但是当我尝试在具有未大小字段的结构上实现相同的特征时出现问题:
So far it all works fine, but then an issue arises when I try to implement the same traits on a struct with unsized fields:
pub struct VirtArray {
_type: Box<dyn DataType>,
value: Vec<Box<dyn DataType>>
}
impl DataType for VirtArray {
fn random() -> Box<Self> {
let t = random_var();
let s = thread_rng().gen_range(0, 10);
Box::new(Self {
_type: *t,
value: (0..s).map(|_| t.gen_another()).collect()
})
}
fn gen_another(&self) -> Box<Self> {
Box::new(Self {
_type: self._type,
value: self.value.iter().map(|t| t.gen_another()).collect::<Vec<Box<dyn DataType>>>()
})
}
}
impl fmt::Debug for VirtArray {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[{:?}; {}]", self._type, self.value.len())
}
}
impl fmt::Display for VirtArray {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut s = self.value.iter().map(|v| format!("{}, ",v)).collect::<String>();
s.truncate(s.len().checked_sub(2).unwrap_or(s.len()));
write!(f, "[{}]", s)
}
}
这多次抛出错误特征'DataType'不能被制成对象
.然后我有一个函数来创建一个随机结构,但也会引发相同的错误:
This throws the error the trait 'DataType' cannot be made into an object
on several occasions. I then have a function to create a random struct but that also throws the same error:
/// generate a random type with random value
fn random_var() -> Box<dyn DataType> {
match thread_rng().gen_range(0,4) {
0 => Box::new(VirtBool::random()),
1 => Box::new(VirtU8::random()),
2 => Box::new(VirtU16::random()),
3 => Box::new(VirtArray::random()),
_ => panic!("invalid")
}
}
我最初使用枚举来完成所有这些,但我试图将其切换到结构和特征以帮助扩展/使用能力.有谁知道如何修复上面的代码?很长一段时间以来,我一直在这里遇到障碍.
I was originally using enums to do all of this but I'm trying to switch it over to structs and traits to help with scaling/use-ability. Does anyone have any idea how to fix the code above? I've been at a roadblock here for quite a while now.
另外,我知道我可以使用 type_name_of_val
来打印类型,但我试图避免使用不稳定/夜间功能.
Also, I'm aware I could use type_name_of_val
to print the types, but I'm trying to keep from using unstable/nightly features.
推荐答案
DataType
不能被做成 trait 对象,因为它使用了 Self
并且因为它有一个静态方法.
DataType
cannot be made into a trait object because it uses Self
and because it has a static method.
我意识到在 dyn DataType
上调用返回 Box
似乎是合理的,因为如果你在 dyn DataType<上调用它/code> 你想要一个
Box
,但 Rust 不会尝试修改方法来让你转换返回的方法,例如Box
转换为返回 Box
如果在 dyn DataType
值上调用它们.您可以通过让方法返回 Box
来解决此问题.
I realize it might seem like returning Box<Self>
may be reasonable to call on a dyn DataType
, since if you call it on dyn DataType
you want a Box<dyn DataType>
, but Rust doesn't try to modify methods for you to turn methods that return e.g. Box<VirtArray>
into ones that return Box<dyn DataType>
if they are called on a dyn DataType
value. You can work around this by having the methods return Box<dyn DataType>
instead.
trait 对象不允许使用静态方法,因为 trait 对象类型没有实现.记住,dyn Foo
实现了 Foo
.(dyn DataType)::random()
是什么?(您可以使用 where 子句,如上例所示,以确保 dyn DataType
不会以可提前检测的方式使用此方法;这意味着您可以'不要在 dyn DataType
对象上使用它,但听起来您可能不想这样做.)
Static methods are not allowed for trait objects because there is no implementation for the trait object type. Remember, dyn Foo
implements Foo
. What would (dyn DataType)::random()
be? (You can use a where clause, as in the example above, to make sure dyn DataType
isn't expected to have this method in a way that can be detected ahead of time; this means you can't use it on your dyn DataType
objects, but it sounds like you might not want to.)
这篇关于Rust trait 问题 trait 不能变成一个对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!