Rust trait 问题 trait 不能做成一个对象

Rust trait issues trait cannot be made into an object

我正在尝试编写一些代码来生成具有随机值的随机结构。我有以下结构特征和辅助宏:

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)
            }
        }
    };
}

然后我使用宏在一堆结构上实现所需的特征(例如这里的几个):

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");

到目前为止一切正常,但是当我尝试在具有未调整大小的字段的结构上实现相同的特征时出现问题:

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)
    }
}

这会多次引发错误 the trait 'DataType' cannot be made into an object。然后我有一个函数来创建一个随机结构,但它也会抛出同样的错误:

/// 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")
    }
}

我最初使用枚举来完成所有这些,但我正在尝试将其切换到结构和特征以帮助 scaling/use-ability。有谁知道如何修复上面的代码?我在这里遇到障碍已经有一段时间了。

此外,我知道我可以使用 type_name_of_val 打印类型,但我试图避免使用 unstable/nightly 功能。

DataType 不能成为特征对象,因为它使用 Self 并且因为它有一个静态方法。

我意识到在 dyn DataType 上调用 returning Box<Self> 似乎是合理的,因为如果你在 dyn DataType 上调用它,你需要一个 Box<dyn DataType>,但 Rust 不会尝试为您修改方法以转换 return 的方法,例如Box<VirtArray> 变成 return Box<dyn DataType> 如果它们是在 dyn DataType 值上调用的。您可以通过使用方法 return Box<dyn DataType> 来解决这个问题。

特征对象不允许使用静态方法,因为特征对象类型没有实现。请记住,dyn Foo 实现了 Foo(dyn DataType)::random() 会是什么? (您可以使用 where 子句,如上例所示,以确保 dyn DataType 不会以可以提前检测到的方式使用此方法;这意味着您不能在你的 dyn DataType 对象,但听起来你可能不想。)