我可以在 Rust 中实现将信息添加到外部类型的特征吗?
Can I implement a trait which adds information to an external type in Rust?
我刚刚实现了一个简单的特征来保存结构的历史属性:
fn main() {
let mut weight = Weight::new(2);
weight.set(3);
weight.set(5);
println!("Current weight: {}. History: {:?}", weight.value, weight.history);
}
trait History<T: Copy> {
fn set(&mut self, value: T);
fn history(&self) -> &Vec<T>;
}
impl History<u32> for Weight {
fn set(&mut self, value: u32) {
self.history.push(self.value);
self.value = value;
}
fn history(&self) -> &Vec<u32> {
&self.history
}
}
pub struct Weight {
value: u32,
history: Vec<u32>,
}
impl Weight {
fn new(value: u32) -> Weight {
Weight {
value,
history: Vec::new(),
}
}
}
我不认为这是可能的,但你能否将 History
特征(或类似的东西)添加到还没有 history
属性 的东西中(像 u32
或 String
), 有效地附加一些关于变量取值的信息?
没有。 Traits 不能将数据成员添加到现有结构中。实际上,只有程序员可以通过修改结构的定义来做到这一点。包装结构或哈希表是可行的方法。
不,特征只能包含行为,不能包含数据。但是你可以做一个结构。
如果可以为 u32
实现 History
,则必须无限期地保留 每个 u32
对象的整个历史记录,万一有一天有人决定给它打电话.history()
。 (此外,当您将一个 u32
分配给另一个时会发生什么?它的历史是否随之而来,或者新值是否只是添加到列表中?)
相反,您可能希望能够标记特定 u32
对象以保留历史记录。正如 所建议的那样,包装器结构将起作用:
mod hist {
use std::mem;
pub struct History<T> {
value: T,
history: Vec<T>,
}
impl<T> History<T> {
pub fn new(value: T) -> Self {
History {
value,
history: Vec::new(),
}
}
pub fn set(&mut self, value: T) {
self.history.push(mem::replace(&mut self.value, value));
}
pub fn get(&self) -> T
where
T: Copy,
{
self.value
}
pub fn history(&self) -> &[T] {
&self.history
}
}
}
它是通用的,所以你可以有一个 History<u32>
或 History<String>
或任何你想要的,但是 get()
方法只有在包装类型是 Copy
.* 您的 Weight
类型可能只是 History<u32>
的别名。 Here it is in the playground.
将此代码包装在模块中是维护抽象的必要部分。也就是说不能写weight.value
,必须调用weight.get()
。如果 value
被标记为 pub
,您可以直接分配给 weight.value
(绕过 set
),然后 history
将不准确。
作为旁注,你想要&Vec<T>
当你可以使用&[T]
,所以我改变了history()
的签名。您可能会考虑的另一件事是返回对先前值的迭代器(可能以相反的顺序)而不是切片。
* 从 History<T>
中获取 T
的更好方法是实现 Deref
并编写 *foo
而不是 foo.get()
。
我刚刚实现了一个简单的特征来保存结构的历史属性:
fn main() {
let mut weight = Weight::new(2);
weight.set(3);
weight.set(5);
println!("Current weight: {}. History: {:?}", weight.value, weight.history);
}
trait History<T: Copy> {
fn set(&mut self, value: T);
fn history(&self) -> &Vec<T>;
}
impl History<u32> for Weight {
fn set(&mut self, value: u32) {
self.history.push(self.value);
self.value = value;
}
fn history(&self) -> &Vec<u32> {
&self.history
}
}
pub struct Weight {
value: u32,
history: Vec<u32>,
}
impl Weight {
fn new(value: u32) -> Weight {
Weight {
value,
history: Vec::new(),
}
}
}
我不认为这是可能的,但你能否将 History
特征(或类似的东西)添加到还没有 history
属性 的东西中(像 u32
或 String
), 有效地附加一些关于变量取值的信息?
没有。 Traits 不能将数据成员添加到现有结构中。实际上,只有程序员可以通过修改结构的定义来做到这一点。包装结构或哈希表是可行的方法。
不,特征只能包含行为,不能包含数据。但是你可以做一个结构。
如果可以为 u32
实现 History
,则必须无限期地保留 每个 u32
对象的整个历史记录,万一有一天有人决定给它打电话.history()
。 (此外,当您将一个 u32
分配给另一个时会发生什么?它的历史是否随之而来,或者新值是否只是添加到列表中?)
相反,您可能希望能够标记特定 u32
对象以保留历史记录。正如
mod hist {
use std::mem;
pub struct History<T> {
value: T,
history: Vec<T>,
}
impl<T> History<T> {
pub fn new(value: T) -> Self {
History {
value,
history: Vec::new(),
}
}
pub fn set(&mut self, value: T) {
self.history.push(mem::replace(&mut self.value, value));
}
pub fn get(&self) -> T
where
T: Copy,
{
self.value
}
pub fn history(&self) -> &[T] {
&self.history
}
}
}
它是通用的,所以你可以有一个 History<u32>
或 History<String>
或任何你想要的,但是 get()
方法只有在包装类型是 Copy
.* 您的 Weight
类型可能只是 History<u32>
的别名。 Here it is in the playground.
将此代码包装在模块中是维护抽象的必要部分。也就是说不能写weight.value
,必须调用weight.get()
。如果 value
被标记为 pub
,您可以直接分配给 weight.value
(绕过 set
),然后 history
将不准确。
作为旁注,你&Vec<T>
当你可以使用&[T]
,所以我改变了history()
的签名。您可能会考虑的另一件事是返回对先前值的迭代器(可能以相反的顺序)而不是切片。
* 从 History<T>
中获取 T
的更好方法是实现 Deref
并编写 *foo
而不是 foo.get()
。