Reputation: 19592
I'm trying to implement a few operations on a parameterized type (specifically, some common "stack" operations on top of Vec<T>
. However I'm not sure how that works (yet), so here's a stripped-down version of what I'm currently struggling with:
trait Stack<T> {
fn top(&self) -> Option<T>;
}
impl Stack for Vec<T> {
fn top<T>(&self) -> Option<T> {
match self.len() {
0 => None,
n => Some(self[n-1])
}
}
}
fn main() {
let mut stack: Vec<f64> = Vec::new();
stack.push(1324.4);
println!("{}", stack.top());
}
The above fails to compile (on rust nightly) with the following error:
test.rs:6:20: 6:21 error: use of undeclared type name `T`
test.rs:6 impl Stack for Vec<T> {
^
error: aborting due to previous error
Upvotes: 1
Views: 1615
Reputation: 432059
Here are two possible implementations if you want to support returning a value, instead of a reference. Note that you can't have both of them. In most cases, you'll see the one with the Clone
bound, as any type that is Copy
should also implement Clone
:
trait Stack<T> {
fn top(&self) -> Option<T>;
}
// We have values where we duplicate by copying bits naïvely
impl<T> Stack<T> for Vec<T>
where T: Copy
{
fn top(&self) -> Option<T> {
self.last().map(|v| *v)
}
}
// We have values where we can duplicate them,
// but it might take a function call to do so
impl<T> Stack<T> for Vec<T>
where T: Clone
{
fn top(&self) -> Option<T> {
self.last().map(|v| v.clone())
}
}
fn main() {
let stack = vec![1324.4f64];
println!("{:?}", stack.top());
}
Upvotes: 1
Reputation: 14750
This should work:
impl<T> Stack<T> for Vec<T> {
fn top(&self) -> Option<T> {
match self.len() {
0 => None,
n => Some(self[n-1])
}
}
}
You need to inform the compiler about the type parameters of Stack
relevant in the implementation.
But that's not enough: fixing this error brings up an issue with the main
function, specifically the parameter to println!
is of the wrong type:
fn main() {
let mut stack: Vec<f64> = Vec::new();
stack.push(42.0);
match stack.pop() {
None => println!("empty stack"),
Some(n) => println!("top: {}", n)
};
}
But this fix, in turn, shows that the top
method isn't well typed for that code. One way to fix the error:
trait Stack<T> {
fn top(&self) -> Option<&T>;
}
impl<T> Stack<T> for Vec<T> {
fn top(&self) -> Option<&T> {
match self.len() {
0 => None,
n => Some(&self[n-1])
}
}
}
Edit:
as explained by @sellibitze in the comments, the reason it doesn't work is that from the original definition of Stack
, the compiler couldn't know that values of type T
are copyable or at least clonable — ie. that T
supports the Copy
or the Clone
trait, and hence values could not be duplicated (a return by value in C++ parlance). Using a reference solves the problem as a reference to T
is copyable
rather than matching on the return value from top
, I could have used the fact that Option<T>
supports the Show
trait when T
implements it, which happens to be true for f64
. This means that I could simply replace {}
by the {:?}
formatter in the call to the println!
macro, and leave the rest of the original main
function untouched.
For example:
fn main(){
let mut stack: Vec<f64> = Vec::new();
stack.push(42.0);
println!("top: {:?}", stack.top())
}
Upvotes: 2