When I implement my trait, I want to save some information when calling a function, which can be used for next time when use the function again, for example:
No, there's no practical way to do this. You'll have to save the extra data in Foo itself and declare it as part of the struct, or create some kind of wrapper around Foo that will handle storing extra.
If you can describe the context for your question in more detail we can probably suggest a specific solution.
I'm sorry I edit my question to the real question I come up with. Sorry for that if you have to edit your answer too...
What I want is that no matter what the container type it is, I have the push method for it. But in the array case, I need a index to mark where I'v pushed.
A trait tells the Rust compiler about functionality a particular type has and can share with other types. We can use traits to define shared behavior in an abstract way.
As I understand the concept of traits: a trait defines an abstract interface to objects. A trait cannot store runtime information for you. Instead the trait gives you the ability to define common methods to access stored information in an object. An object can be a struct or enum instance.
I guess you are used to inheritance or extension of classes from other languages. Currently you cannot extend structs or enums like you could in Java or C++. You may use a programming pattern called incubation. For example:
// looks almost like a wrapper
impl Push for [u8] {
index : usize,
fn push(&mut self, v: u8) {
self[index++] = v;
index+=1;
}
}
// use a wrapper to associate the push index with the array
fn main() {
let array = [0u8; 10];
assert_eq!(array, [0,0,0,0,0,0,0,0,0,0]);
let mut wraped: ArrayWrapper = array.into();
wraped.push(23);
wraped.push(42);
assert_eq!(wraped.array, [23,42,0,0,0,0,0,0,0,0])
}
struct ArrayWrapper {
// care: push_index should reach every bucket of this index
array: [u8; 10],
// care: maximum size of u8 is 255. This example is ok because 10 < 255
push_index: usize,
}
impl From<[u8; 10]> for ArrayWrapper {
fn from(array: [u8; 10]) -> Self {
Self {
array,
push_index: 0,
}
}
}
trait Push {
fn push(&mut self, value: u8);
}
impl Push for ArrayWrapper {
fn push(&mut self, value: u8) {
self.array[self.push_index] = value;
self.push_index += 1; // beware: this can overflow
}
}
Thanks for the solution, but this got a problem, the trait Push is not implemented on the array type [u8], which leads to that if a function like fn do_something<T: Push>(v : T) will not accept an array as the parameter.
Forgive me if I'm asking too much, with this solution, is there any auto wrapping mechanism (like C++)? I guess the answer is a "no" since Rust do not have "Constructors".
Rust has the traits From and Into. The standard library implements those for several types. In for loops rust automatically uses into_iter(). I personally like that one can opt in explicitly for type conversion, but it most often doesn't do that automatically. This allows us as developers to understand the internals without too much headache about when what is transformed. It is almost always written explicitly.
The symmetric Into is implemented automatically for every type which implements From.
I updated the example above so you may have a look at it. I'd suggest not to use arrays though. Arrays are very low level and bugs will creep into your code easily. See it this way: rust protected you from making a hard to find mistake: An array cannot natively store something like an insert position. It is merely a list of values stored in sequence in memory. That is why the insert position must be stored somewhere else. But then again every concrete insert position must only be used with its corresponding array. Otherwise the insert position will become inconsistent. This inconsistency will be very hard to figure out if you are not aware of it beforehand.
It sounds like you are running into some friction because you want to apply an inheritance-based solution to a language which deliberately doesn't permit inheritance. This would be like trying to force immutability/const-correctness onto Java - it's doable, but a massive pain.
When you want to add extra requirements and invariants on top of an existing type you'll normally make a new type which holds the original type as a private field. Then you can provide an API which presents just the functionality you need and manages the book keeping.
In this case you can also implement Deref to give people read-only access to the vector being wrapped seeing as your new type is just a smart wrapper around Vec<T>.