简体   繁体   English

我可以实现一个将信息添加到Rust中的外部类型的特征吗?

[英]Can I implement a trait which adds information to an external type in Rust?

I just implemented a simple trait to keep the history of a struct property: 我刚刚实现了一个简单的特性来保存struct属性的历史:

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(),
        }
    }
}

I don't expect this is possible, but could you add the History trait (or something equivalent) to something which doesn't already have a history property (like u32 or String ), effectively tacking on some information about which values the variable has taken? 我不认为这是可能的,但你可以将History特征(或类似的东西)添加到尚未具有history属性的东西(如u32String ),有效地处理有关变量具有哪些值的一些信息有人吗?

No. Traits cannot add data members to the existing structures. 否。特征无法将数据成员添加到现有结构中。 Actually, only a programmer can do that by modifying the definition of a structure. 实际上,只有程序员才能通过修改结构的定义来做到这一点。 Wrapper structures or hash-tables are the ways to go. 包装结构或散列表是可行的方法。

No, traits can only contain behavior, not data. 不,特征只能包含行为,而不能包含数据。 But you could make a struct. 但你可以制作一个结构。

If you could implement History for u32 , you'd have to keep the entire history of every u32 object indefinitely, in case one day someone decided to call .history() on it. 如果你可以为u32实现History ,你必须无限期地保留每个 u32对象的整个历史记录,以防有一天有人决定在它上面调用.history() (Also, what would happen when you assign one u32 to another? Does its history come with it, or does the new value just get added to the list?) (另外,当你将一个u32分配给另一个时会发生什么?它的历史记录是否附带它,或新的值是否刚刚添加到列表中?)

Instead, you probably want to be able to mark specific u32 objects to keep a history. 相反,您可能希望能够标记特定的 u32对象以保留历史记录。 A wrapper struct, as red75prime's answer suggests, will work: 正如red75prime的回答所示,包装结构将起作用:

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
        }
    }
}

It's generic, so you can have a History<u32> or History<String> or whatever you want, but the get() method will only be implemented when the wrapped type is Copy .* Your Weight type could just be an alias for History<u32> . 它是通用的,因此您可以拥有History<u32>History<String>或任何您想要的内容,但get()方法仅在包装类型为Copy 。*您的Weight类型可能只是History<u32>的别名History<u32> Here it is in the playground. 这是在操场上。

Wrapping this code in a module is a necessary part of maintaining the abstraction. 将此代码包装在模块中是维护抽象的必要部分。 That means you can't write weight.value , you have to call weight.get() . 这意味着你不能写weight.value ,你必须调用weight.get() If value were marked pub , you could assign directly to weight.value (bypassing set ) and then history would be inaccurate. 如果value标记为pub ,则可以直接指定weight.value (绕过set ),然后history将不准确。

As a side note, you almost never want &Vec<T> when you can use &[T] , so I changed the signature of history() . 作为旁注,当你可以使用&[T]时,你几乎不会想要&Vec<T> ,所以我改变了history()的签名。 Another thing you might consider is returning an iterator over the previous values (perhaps in reverse order) instead of a slice. 您可能会考虑的另一件事是返回迭代器而不是先前的值(可能是相反的顺序)而不是切片。


* A better way of getting the T out of a History<T> is to implement Deref and write *foo instead of foo.get() . *从History<T>中获取T更好方法是实现Deref并编写*foo而不是foo.get()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM