首页 文章

无法克隆Vec <Box <Trait >>因为Trait无法成为对象

提问于
浏览
6

我正在尝试克隆盒装特征的向量 . 自然地简单地在实现我的特征的所有结构上派生 Clone 并不是在编译时知道实现该特征的所有结构都具有 Clone .

好吧,所以我接着尝试使用 Clone 作为超级画面,但这只会导致 Headers 中的错误 . 我对解决方案感到茫然 .

这是最小工作实现(或不工作,因为我无法克隆)

#![allow(dead_code, unused_macros)]
use std::fmt::Debug;

trait MusicElement: Debug + Clone {
    fn duration(&self) -> f32;
}

#[derive(Debug, Clone)]
struct Note<'a> {
    name: &'a str,
    duration: f32,
}

impl<'a> MusicElement for Note<'a> {
    fn duration(&self) -> f32 {
        self.duration
    }
}

#[derive(Debug, Clone)]
struct Pause {
    duration: f32,
}

impl MusicElement for Pause {
    fn duration(&self) -> f32 {
        self.duration
    }
}

#[derive(Debug, Clone)]
struct Sequence {
    elements: Vec<Box<MusicElement>>,
}

impl MusicElement for Sequence {
    fn duration(&self) -> f32 {
        self.elements.iter().map(|e| e.duration()).sum()
    }
}

fn main() {
    let a4 = |dur| Box::new(Note { name: "a4", duration: dur });
    let seq = Sequence { elements: vec![a4(0.25), a4(0.25), a4(0.5)] };
    println!("{:?}", seq);
    let seq2 = seq.clone();
    println!("{:?}", seq2);
}

出现此错误:

error[E0038]: the trait `MusicElement` cannot be made into an object
  --> src/main.rs:33:5
   |
33 |     elements: Vec<Box<MusicElement>>,
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `MusicElement` cannot be made into an object
   |
   = note: the trait cannot require that `Self : Sized`

这是一个容易运行的代码link to the playground .

我也试图在 Sequence Vec<Box<MusicElement + Clone>> 中制作 elements 向量,但这也不起作用 .

我无法在网上找到任何有用的解决方案,所以这是我的问题:如何使代码可以克隆?

2 回答

  • 6

    解决方案在于结合迄今为止的评论中的建议 - @Lukas Kalbertodt's comment中的答案告诉您必须为所有兼容( 'static + MusicElement + Clone )类型创建一个全面的特征实现 . 实现所需的唯一后续步骤是将 Note.name 字段的类型从 &'a str 更改为 Stringas metioned by @Boiethios

    #![allow(dead_code, unused_macros)]
    use std::fmt::Debug;
    
    trait MusicElement: MusicElementClone + Debug {
        fn duration(&self) -> f32;
    }
    
    trait MusicElementClone {
        fn clone_box(&self) -> Box<MusicElement>;
    }
    
    impl<T: 'static + MusicElement + Clone> MusicElementClone for T {
        fn clone_box(&self) -> Box<MusicElement> {
            Box::new(self.clone())
        }
    }
    
    impl Clone for Box<MusicElement> {
        fn clone(&self) -> Box<MusicElement> {
            self.clone_box()
        }
    }
    
    #[derive(Debug, Clone)]
    struct Note {
        name: String,
        duration: f32,
    }
    
    impl MusicElement for Note {
        fn duration(&self) -> f32 {
            self.duration
        }
    }
    
    #[derive(Debug, Clone)]
    struct Pause {
        duration: f32,
    }
    
    impl MusicElement for Pause {
        fn duration(&self) -> f32 {
            self.duration
        }
    }
    
    #[derive(Debug, Clone)]
    struct Sequence {
        elements: Vec<Box<MusicElement>>,
    }
    
    impl MusicElement for Sequence {
        fn duration(&self) -> f32 {
            self.elements.iter().map(|e| e.duration()).sum()
        }
    }
    
    fn main() {
        let a4 = |dur| Box::new(Note { name: String::from("a4"), duration: dur });
        let seq = Sequence { elements: vec![a4(0.25), a4(0.25), a4(0.5)] };
        println!("{:?}", seq);
        let seq2 = seq.clone();
        println!("{:?}", seq2);
    }
    

    编译,所以它应该足够了!

  • 1

    我的objekt crate提供了jonny's answer的可重用实现 . 有了它,您可以使您的原始代码只需最少的更改即可运行 .


    之前:

    trait MusicElement: Debug + Clone {
        fn duration(&self) -> f32;
    }
    

    之后:

    #[macro_use]
    extern crate objekt;
    
    trait MusicElement: Debug + objekt::Clone {
        fn duration(&self) -> f32;
    }
    
    clone_trait_object!(MusicElement);
    
    // Everything else as you wrote it.
    

相关问题