Search code examples
rustvectortraits

How to implement a trait on a Vec that can append a string?


This is from trying Rustlings Exercise Traits2. The exercise requires me to implement the trait on a Vec. The tests are there and they are failing, which is a good place to start. I've done the trait implementation for the String and it was easy, but Vec is another story. I'm not sure what the method needs to return, it fails on various returns. I'm providing original code, my attempt and the errors i get for my attempt. Hopefully that will be enough.

Original Code from the Rustlings repo:

// traits2.rs
// 
// Your task is to implement the trait
// `AppendBar' for a vector of strings.
// 
// To implement this trait, consider for
// a moment what it means to 'append "Bar"'
// to a vector of strings.
// 
// No boiler plate code this time,
// you can do this!

// I AM NOT DONE

trait AppendBar {
    fn append_bar(self) -> Self;
}

//TODO: Add your code here




#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn is_vec_pop_eq_bar() {
        let mut foo = vec![String::from("Foo")].append_bar();
        assert_eq!(foo.pop().unwrap(), String::from("Bar"));
        assert_eq!(foo.pop().unwrap(), String::from("Foo"));
    }

}

and my attempt to solve it:

// traits2.rs
//
// Your task is to implement the trait
// `AppendBar' for a vector of strings.
//
// To implement this trait, consider for
// a moment what it means to 'append "Bar"'
// to a vector of strings.
//
// No boiler plate code this time,
// you can do this!

// I AM NOT DONE
use std::clone::Clone;
trait AppendBar {
    fn append_bar(&mut self) -> Self;
}

//TODO: Add your code here
impl<T: Clone> AppendBar for Vec<T> {
    fn append_bar(&mut self) -> Self {
        let bar: T = String::from("Bar");
        self.to_vec().push(bar)
        // self.to_vec()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn is_vec_pop_eq_bar() {
        let mut foo = vec![String::from("Foo")].append_bar();
        assert_eq!(foo, vec![String::from("Foo"), String::from("Bar")]);
        assert_eq!(foo.pop().unwrap(), String::from("Bar"));
        assert_eq!(foo.pop().unwrap(), String::from("Foo"));
    }
}

Which compiles to an error:

! Compiling of exercises/traits/traits2.rs failed! Please try again. Here's the output:
error[E0308]: mismatched types
  --> exercises/traits/traits2.rs:22:22
   |
20 | impl<T: Clone> AppendBar for Vec<T> {
   |      - this type parameter
21 |     fn append_bar(&mut self) -> Self {
22 |         let bar: T = String::from("Bar");
   |                  -   ^^^^^^^^^^^^^^^^^^^ expected type parameter `T`, found struct `std::string::String`
   |                  |
   |                  expected due to this
   |
   = note: expected type parameter `T`
                      found struct `std::string::String`
   = help: type parameters must be constrained to match other types
   = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters

error[E0308]: mismatched types
  --> exercises/traits/traits2.rs:23:9
   |
21 |     fn append_bar(&mut self) -> Self {
   |                                 ---- expected `std::vec::Vec<T>` because of return type
22 |         let bar: T = String::from("Bar");
23 |         self.to_vec().push(bar)
   |         ^^^^^^^^^^^^^^^^^^^^^^^ expected struct `std::vec::Vec`, found `()`
   |
   = note: expected struct `std::vec::Vec<T>`
           found unit type `()`

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0308`.

I've read and re-read the suggested part and the traits in the book but it's beyond me. I'm sure it's a simple solution to it but i can't see it.


Solution

  • There are several issues:

    • You try to push a String to generic Vec<T>, where T can be any type!
    • The method signature is different from the assignment: Your method is defined as
      fn append_bar(&mut self) -> Self
      
      but it should be
      fn append_bar(self) -> Self
      
    • You try to return the result of Vec::push, but this method doesn't return anything.

    To fix the first issue, implement the trait for Vec<String> instead of Vec<T>. That's what the assignment is asking for:

    // Your task is to implement the trait
    // `AppendBar' for a vector of strings.
    

    To fix the second issue, you have to remove the &, so the method accepts an owned value.

    To fix the last issue, return self after calling Vec::push in a separate statement:

    self.push(bar);
    self