Rust trait 问题 trait 不能变成一个对象 [英] Rust trait issues trait cannot be made into an object

查看:78
本文介绍了Rust trait 问题 trait 不能变成一个对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆