来自引用的特征对象
Trait objects from references
下面是(大致)取自 rust book chapter 17.2 的 trait object 示例。对于我的用例,我想在创建 screen
后继续使用 button
和 select_box
(请参阅声明 screen
后注释掉的 println!()
),但是我我不能,因为 button
和 select_box
移到了 screen
。在我看来,解决方案似乎是 screen
借用 select_box
和 screen
,而不是取得所有权。但是,我不知道该怎么做。我试过从以下参考创建框:
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 函数可以引用两个组件 select_box
和 button
同时
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(";")
)
}
}
这是输出:
button width: 50
SelectBox(75, 10, Yes;Maybe;No)
Button(50, 10, OK)
Trait 对象可以通过 any 指针类型(如引用、Rc
、Arc
等)使用,而不仅仅是 Box
.所以,如果你想 Screen
借用组件,你可以简单地存储 references 到代表组件的 Draw
特征对象:
struct Screen<'c> {
components: Vec<&'c dyn Draw>,
}
impl Screen<'_> {
fn run(&self) {
for component in self.components.iter() {
component.draw();
}
}
}
下面是(大致)取自 rust book chapter 17.2 的 trait object 示例。对于我的用例,我想在创建 screen
后继续使用 button
和 select_box
(请参阅声明 screen
后注释掉的 println!()
),但是我我不能,因为 button
和 select_box
移到了 screen
。在我看来,解决方案似乎是 screen
借用 select_box
和 screen
,而不是取得所有权。但是,我不知道该怎么做。我试过从以下参考创建框:
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 函数可以引用两个组件 select_box
和 button
同时
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(";")
)
}
}
这是输出:
button width: 50
SelectBox(75, 10, Yes;Maybe;No)
Button(50, 10, OK)
Trait 对象可以通过 any 指针类型(如引用、Rc
、Arc
等)使用,而不仅仅是 Box
.所以,如果你想 Screen
借用组件,你可以简单地存储 references 到代表组件的 Draw
特征对象:
struct Screen<'c> {
components: Vec<&'c dyn Draw>,
}
impl Screen<'_> {
fn run(&self) {
for component in self.components.iter() {
component.draw();
}
}
}