简体   繁体   中英

Use of undeclared type name error with a parameterized trait

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

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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