将实现特征的数据存储在向量中
Store data that implements a trait in a vector
总的来说,我对 Rust 和系统语言还很陌生。我目前正在研究 Rust 来探索这门语言。我有一个我无法自己解决的问题。而且我想我已经了解发生了什么问题。
我不想在向量中存储实现 trait BaseStuff
的对象。在 Rust 中对我来说不是一个简单的任务 :-).
这是我无法编译的示例代码。
trait BaseStuff {}
struct MyStuff {
value: i32,
}
struct AwesomeStuff {
value: f32,
text: String,
}
impl BaseStuff for MyStuff {}
impl BaseStuff for AwesomeStuff {}
struct Holder {
stuff: Vec<BaseStuff>,
}
impl Holder {
fn register(&mut self, data: impl BaseStuff) {
self.stuff.push(data);
}
}
fn main() {
let my_stuff = MyStuff { value: 100 };
let awesome_stuff = AwesomeStuff {
value: 100.0,
text: String::from("I'm so awesome!"),
};
let mut holder = Holder { stuff: vec![] };
holder.register(my_stuff);
}
error[E0277]: the size for values of type (dyn BaseStuff + 'static)
cannot be known at compilation time --> src\main.rs:17:5 | 17 |
stuff: Vec, // unknown size | ^^^^^^^^^^^^^^^^^^^^^
doesn't have a size known at compile-time | = help: the trait
std::marker::Sized
is not implemented for (dyn BaseStuff +
'static)
= note: to learn more, visit
https://doc.rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait
= note: required by std::vec::Vec
error: aborting due to previous error
For more information about this error, try rustc --explain E0277
.
error: Could not compile playground
.
The compiler message is clear and I understand the message. I can
implement the trait BaseStuff in any struct I want't so its unclear
which size it is. Btw the link isn't helpful because its pointing to
outdated site...
String 的大小也是未知的,但是 String 实现了特征 std::marker::Sized
,这就是为什么 Vec<String>
可以正常工作的原因。对吗?
在我读到的关于大小未知的数据类型的 rust 书中,我必须将这些数据存储在堆上而不是堆栈上。我更改了我的代码如下。
struct Holder {
stuff: Vec<Box<BaseStuff>>,
}
impl Holder {
fn register(&mut self, data: impl BaseStuff) {
self.stuff.push(Box::new(data));
}
}
现在我遇到了一个新的编译器问题:
error[E0310]: the parameter type impl BaseStuff
may not live long
enough --> src\main.rs:22:25 | 21 | fn register(&mut self,
data: impl BaseStuff) { |
--------------- help: consider adding an explicit lifetime bound impl BaseStuff: 'static
... 22 | self.stuff.push(Box::new(data));
| ^^^^^^^^^^^^^^ | note: ...so that the
type impl BaseStuff
will meet its required lifetime bounds -->
src\main.rs:22:25 | 22 | self.stuff.push(Box::new(data));
| ^^^^^^^^^^^^^^
error: aborting due to previous error
For more information about this error, try rustc --explain E0310
.
error: Could not compile playground
.
知道我出局了……我读了关于生命周期的书,并用 'a
在这里和 'a
任意组合更改了我的代码,但没有运气……我不想写下我尝试过的任何生命周期定义组合。
我不明白为什么我需要生命周期定义。所有权在任何步骤中都会移动,因此据我了解,很明显 Holder 结构是所有数据的所有者。是吗?
如何更正我的代码以进行编译?
感谢您的帮助。
你几乎明白了——这里的问题是实现BaseStuff
的类型可能是一个引用(例如impl BaseStuff for &SomeType
)。这意味着即使您按值传递 data
,该值也可能是一个引用,您的 Box
.
将不再存在
解决这个问题的方法是添加一个约束,使对象具有 'static
生命周期,这意味着它要么是值类型,要么是静态引用。您可以将此约束应用于特征或接受特征的方法,具体取决于您的用例。
将约束应用于特征:
trait BaseStuff: 'static {}
将约束应用于方法:
impl Holder {
fn register(&mut self, data: impl BaseStuff + 'static) {
self.stuff.push(Box::new(data));
}
}
如果将 'static
约束添加到方法中,我建议也将其添加到 Vec
中以避免丢失类型信息,如下所示:
struct Holder {
stuff: Vec<Box<dyn BaseStuff + 'static>>,
}
总的来说,我对 Rust 和系统语言还很陌生。我目前正在研究 Rust 来探索这门语言。我有一个我无法自己解决的问题。而且我想我已经了解发生了什么问题。
我不想在向量中存储实现 trait BaseStuff
的对象。在 Rust 中对我来说不是一个简单的任务 :-).
这是我无法编译的示例代码。
trait BaseStuff {}
struct MyStuff {
value: i32,
}
struct AwesomeStuff {
value: f32,
text: String,
}
impl BaseStuff for MyStuff {}
impl BaseStuff for AwesomeStuff {}
struct Holder {
stuff: Vec<BaseStuff>,
}
impl Holder {
fn register(&mut self, data: impl BaseStuff) {
self.stuff.push(data);
}
}
fn main() {
let my_stuff = MyStuff { value: 100 };
let awesome_stuff = AwesomeStuff {
value: 100.0,
text: String::from("I'm so awesome!"),
};
let mut holder = Holder { stuff: vec![] };
holder.register(my_stuff);
}
error[E0277]: the size for values of type
(dyn BaseStuff + 'static)
cannot be known at compilation time --> src\main.rs:17:5 | 17 |
stuff: Vec, // unknown size | ^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | = help: the traitstd::marker::Sized
is not implemented for(dyn BaseStuff + 'static)
= note: to learn more, visit https://doc.rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait = note: required bystd::vec::Vec
error: aborting due to previous error
For more information about this error, try
rustc --explain E0277
. error: Could not compileplayground
.The compiler message is clear and I understand the message. I can implement the trait BaseStuff in any struct I want't so its unclear which size it is. Btw the link isn't helpful because its pointing to outdated site...
String 的大小也是未知的,但是 String 实现了特征 std::marker::Sized
,这就是为什么 Vec<String>
可以正常工作的原因。对吗?
在我读到的关于大小未知的数据类型的 rust 书中,我必须将这些数据存储在堆上而不是堆栈上。我更改了我的代码如下。
struct Holder {
stuff: Vec<Box<BaseStuff>>,
}
impl Holder {
fn register(&mut self, data: impl BaseStuff) {
self.stuff.push(Box::new(data));
}
}
现在我遇到了一个新的编译器问题:
error[E0310]: the parameter type
impl BaseStuff
may not live long enough --> src\main.rs:22:25 | 21 | fn register(&mut self, data: impl BaseStuff) { |
--------------- help: consider adding an explicit lifetime boundimpl BaseStuff: 'static
... 22 | self.stuff.push(Box::new(data));
| ^^^^^^^^^^^^^^ | note: ...so that the typeimpl BaseStuff
will meet its required lifetime bounds --> src\main.rs:22:25 | 22 | self.stuff.push(Box::new(data));
| ^^^^^^^^^^^^^^error: aborting due to previous error
For more information about this error, try
rustc --explain E0310
. error: Could not compileplayground
.
知道我出局了……我读了关于生命周期的书,并用 'a
在这里和 'a
任意组合更改了我的代码,但没有运气……我不想写下我尝试过的任何生命周期定义组合。
我不明白为什么我需要生命周期定义。所有权在任何步骤中都会移动,因此据我了解,很明显 Holder 结构是所有数据的所有者。是吗?
如何更正我的代码以进行编译?
感谢您的帮助。
你几乎明白了——这里的问题是实现BaseStuff
的类型可能是一个引用(例如impl BaseStuff for &SomeType
)。这意味着即使您按值传递 data
,该值也可能是一个引用,您的 Box
.
解决这个问题的方法是添加一个约束,使对象具有 'static
生命周期,这意味着它要么是值类型,要么是静态引用。您可以将此约束应用于特征或接受特征的方法,具体取决于您的用例。
将约束应用于特征:
trait BaseStuff: 'static {}
将约束应用于方法:
impl Holder {
fn register(&mut self, data: impl BaseStuff + 'static) {
self.stuff.push(Box::new(data));
}
}
如果将 'static
约束添加到方法中,我建议也将其添加到 Vec
中以避免丢失类型信息,如下所示:
struct Holder {
stuff: Vec<Box<dyn BaseStuff + 'static>>,
}