rust 有没有一种方法可以将trait实现的结构体设置为结构体字段?

trnvg8h3  于 2023-06-30  发布在  其他
关注(0)|答案(1)|浏览(128)

我尝试使用trait变量而不将其装箱。
像下面这样,创建trait实现的struct示例而不装箱是很清楚的,但是我不能将这个trait实现的示例设置为其他struct的字段。
我知道使用泛型可以解决这个问题,(比如struct UserCertification\<T\> { current_work: T }
但我不想用它。
有没有其他方法可以使用trait实现的示例而不将其装箱?

#[derive(Copy, Clone)]
enum Job { Teacher, Police }

trait Namable {
  fn name(&self) -> &'static str;
}

// Sample trait-implemented struct 1
struct TemporaryTeacher { }

impl Namable for TemporaryTeacher {
  fn name(&self) -> &'static str { "T.Teacher" }
}

// Sample trait-implemented struct 2
struct GeneralTeacher { }

impl Namable for GeneralTeacher {
  fn name(&self) -> &'static str { "G.Teacher" }
}

// Returns the instance wihtout Box.
fn create_named_object(job: Job, name: &str) -> impl Namable {
  TemporaryTeacher() // one of the result that implements trait Namable.
}

struct UserCertification {
  name: String,
  current_work: dyn Namable, // <- ERROR: cannot use dynamic directly. use Box<dyn Namable>.
}

fn main() {
  let named_object = create_named_object(Job::Teacher, "");
  let user = UserCertification("John", named_object);
}

我试图找出如何创建示例没有装箱过程。为了解决这个问题,我搜索并发现使用泛型可以处理这个问题,但它不是一个理想的解决方案。

oyxsuwqo

oyxsuwqo1#

只有三种方法可以在Rust中完成您正在尝试的工作。每种方式都有自己的权衡,所以哪种更好将取决于您的用例。

使用泛型

trait Namable {}

struct UserCertification<T> {
    current_work: T,
}

impl<T: Namable> UserCertification<T> {
    fn new (current_work: T) -> Self {
        UserCertification { current_work, }
    }
}

这可能是最简单的方法,也是最有效的内存使用和性能。主要的缺点是,你不能在同一代码中混合使用多种类型,也不能动态地更改存储在变量中的类型:

struct Foo;
impl Namable for Foo {}
struct Bar;
impl Namable for Bar {}

// Impossible:
let v = vec![ UserCertification::new (Foo), UserCertification::new (Bar), ];

// Impossible too:
let mut v = UserCertification::new (Foo);
v = UserCertification::new (Bar);

Playground

使用trait对象

trait对象写为dyn Namable,并且它 * 必须 * 在某种引用后面,例如。&dyn NamableBox<dyn Namable>Rc<dyn Namable>Arc<dyn Namable>

trait Namable {}

struct UserCertification {
    current_work: Box<dyn Namable>,
}

impl UserCertification {
    fn new<T: Namable + 'static> (current_work: T) -> Self {
        UserCertification { current_work: Box::new (current_work), }
    }
}

Playground
这相当容易使用,允许非常广泛的用途,包括混合不同的类型和动态更改,但它也是三种解决方案中最慢的。这也是大多数高级语言做事情的方式(例如Java、Python等)。但是some traits cannot be made into trait objects

使用枚举

trait Namable {
    fn name (&self) -> &'static str;
}

struct Foo;
impl Namable for Foo {
    fn name (&self) -> &'static str { "Foo" }
}
struct Bar;
impl Namable for Bar {
    fn name (&self) -> &'static str { "Bar" }
}

enum AllNamables {
    Foo (Foo),
    Bar (Bar),
}
impl Namable for AllNamables {
    fn name (&self) -> &'static str {
        match self {
            AllNamables::Foo (f) => f.name(),
            AllNamables::Bar (b) => b.name(),
        }
    }
}
impl From<Foo> for AllNamables {
    fn from (f: Foo) -> Self { AllNamables::Foo (f) }
}
impl From<Bar> for AllNamables {
    fn from (b: Bar) -> Self { AllNamables::Bar (b) }
}

struct UserCertification {
    current_work: AllNamables,
}

impl UserCertification {
    fn new<T: Into<AllNamables>> (current_work: T) -> Self {
        UserCertification { current_work: current_work.into(), }
    }
}

Playground
这种解决方案允许混合不同的类型并动态更改,它通常比现代CPU上的trait对象解决方案更快。但是:

  • 它需要预先知道您需要的所有可能类型。
  • 它需要大量的样板文件(尽管像enum_dispatch这样的板条箱可能会有所帮助)。

相关问题