简体   繁体   English

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

This throws the error the trait 'DataType' cannot be made into an object on several occasions.这会引发错误, the trait 'DataType' cannot be made into an object多次the trait 'DataType' cannot be made into an object 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.我在这里遇到了一个障碍已经有一段时间了。

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.另外,我知道我可以使用type_name_of_val来打印类型,但我试图避免使用不稳定/夜间功能。

DataType cannot be made into a trait object because it uses Self and because it has a static method. DataType不能被做成 trait 对象,因为它使用Self并且因为它有一个静态方法。

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 eg Box<VirtArray> into ones that return Box<dyn DataType> if they are called on a dyn DataType value.我意识到返回Box<Self>似乎可以合理地调用dyn DataType ,因为如果您在dyn DataType上调用它,您需要一个Box<dyn DataType> ,但 Rust 不会尝试为您修改方法将返回例如Box<VirtArray>转换为返回Box<dyn DataType>如果它们在dyn DataType值上被调用。 You can work around this by having the methods return Box<dyn DataType> instead.您可以通过让方法返回Box<dyn DataType>来解决此问题。

Static methods are not allowed for trait objects because there is no implementation for the trait object type. trait 对象不允许使用静态方法,因为 trait 对象类型没有实现。 Remember, dyn Foo implements Foo .请记住, dyn Foo实现了Foo What would (dyn DataType)::random() be? (dyn DataType)::random()会是什么? (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.) (您可以使用 where 子句,如上例所示,以确保dyn DataType不会以可以提前检测到的方式使用此方法;这意味着您不能在dyn DataType上使用它对象,但听起来您可能不想这样做。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM