簡體   English   中英

來自引用的特征對象

[英]Trait objects from references

下面是(大致)特征 object 示例,取自 rust 書第 17.2 章。 對於我的用例,我想在創建screen后繼續使用buttonselect_box (請參閱聲明screen后注釋掉的println!() ),但是我不能,因為buttonselect_box已移入screen 對我來說,解決方案似乎是讓screen借用select_boxscreen而不是擁有所有權。 但是,我無法弄清楚如何做到這一點。 我嘗試從以下引用創建框:

let screen = Screen {
    components: vec![Box::new(&select_box), Box::new(&button)],
};

但這會產生如下錯誤:

the trait `Draw` is not implemented for `&SelectBox`
fn main() {
    let select_box = SelectBox {
        width: 75,
        height: 10,
        options: vec![
            String::from("Yes"),
            String::from("Maybe"),
            String::from("No"),
        ],
    };
    let button = Button {
        width: 50,
        height: 10,
        label: String::from("OK"),
    };
    let screen = Screen {
        components: vec![Box::new(select_box), Box::new(button)],
    };
    // println!("button width: {}", button.width);
    screen.run();
}

trait Draw {
    fn draw(&self);
}

struct Screen {
    components: Vec<Box<dyn Draw>>,
}

impl Screen {
    fn run(&self) {
        for component in self.components.iter() {
            component.draw();
        }
    }
}

struct Button {
    width: u32,
    height: u32,
    label: String,
}

impl Draw for Button {
    fn draw(&self) {
        println!("Button({}, {}, {})", self.width, self.height, self.label)
    }
}

struct SelectBox {
    width: u32,
    height: u32,
    options: Vec<String>,
}

impl Draw for SelectBox {
    fn draw(&self) {
        println!(
            "SelectBox({}, {}, {})",
            self.width,
            self.height,
            self.options.join(";")
        )
    }
}

通常的解決方案是對&T&mut T進行全面實現,其中T: Draw

impl<T: ?Sized + Draw> Draw for &'_ T {
    fn draw(&self) {
        <T as Draw>::draw(&**self)
    }
}
impl<T: ?Sized + Draw> Draw for &'_ mut T {
    fn draw(&self) {
        <T as Draw>::draw(&**self)
    }
}

但是,然后您會收到另一個錯誤:

error[E0597]: `select_box` does not live long enough
  --> src/main.rs:17:35
   |
17 |         components: vec![Box::new(&select_box), Box::new(&button)],
   |                          ---------^^^^^^^^^^^-
   |                          |        |
   |                          |        borrowed value does not live long enough
   |                          cast requires that `select_box` is borrowed for `'static`
...
21 | }
   | - `select_box` dropped here while still borrowed

error[E0597]: `button` does not live long enough
  --> src/main.rs:17:58
   |
17 |         components: vec![Box::new(&select_box), Box::new(&button)],
   |                                                 ---------^^^^^^^-
   |                                                 |        |
   |                                                 |        borrowed value does not live long enough
   |                                                 cast requires that `button` is borrowed for `'static`
...
21 | }
   | - `button` dropped here while still borrowed

這是因為dyn Trait實際上是dyn Trait + 'static 您需要添加一個生命周期參數:

struct Screen<'a> {
    components: Vec<Box<dyn Draw + 'a>>,
}

impl Screen<'_> {

游樂場

您可以使用Rc代替Box使screen和 main function 可以同時引用兩個組件select_boxbutton

use std::rc::Rc;
fn main() {
    let select_box = Rc::new(SelectBox {
        width: 75,
        height: 10,
        options: vec![
            String::from("Yes"),
            String::from("Maybe"),
            String::from("No"),
        ],
    });
    let button = Rc::new(Button {
        width: 50,
        height: 10,
        label: String::from("OK"),
    });
    let screen = Screen {
        components: vec![select_box.clone(), button.clone()],
    };
    println!("button width: {}", button.width);
    screen.run();
}

trait Draw {
    fn draw(&self);
}

struct Screen {
    components: Vec<Rc<dyn Draw>>,
}

impl Screen {
    fn run(&self) {
        for component in self.components.iter() {
            component.draw();
        }
    }
}

struct Button {
    width: u32,
    height: u32,
    label: String,
}

impl Draw for Button {
    fn draw(&self) {
        println!("Button({}, {}, {})", self.width, self.height, self.label)
    }
}

struct SelectBox {
    width: u32,
    height: u32,
    options: Vec<String>,
}

impl Draw for SelectBox {
    fn draw(&self) {
        println!(
            "SelectBox({}, {}, {})",
            self.width,
            self.height,
            self.options.join(";")
        )
    }
}

操場

這是 output:

button width: 50
SelectBox(75, 10, Yes;Maybe;No)
Button(50, 10, OK)

Trait 對象可以通過任何指針類型(如引用、 RcArc等)使用,而不僅僅是Box 因此,如果您希望Screen借用組件,您可以簡單地存儲對表示組件的Draw trait 對象的引用

struct Screen<'c> {
    components: Vec<&'c dyn Draw>,
}

impl Screen<'_> {
    fn run(&self) {
        for component in self.components.iter() {
            component.draw();
        }
    }
}

操場

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM