简体   繁体   中英

Rustlings Exercise Traits2, Implement Trait on Vec

The exercise requires me to Implement the trait to 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, 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 atempt. Hopefull 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.

There's a discrepancy to Aloso's answer. Andre gives it too.

When you take in self :

fn append_bar(self) -> Self {
    self.push("Bar".to_owned());
    self
}

You are taking in a mutable Vec :

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"));

Even though the Variable foo is declared to be mutable, the method append_bar() takes in a immutable variable. You don't need to borrow self because you're not trying to take full ownership, you're trying to modify the existing data residing in said variable. The correct answer is

fn append_bar(mut self) -> Self {
    self.push("Bar".to_owned()); // || .to_string() || String::from("Bar") 
    // Whatever gets the point across. As the String literal is essentially a "Borrowed" string.
    self
}

Within the scope of append_bar() you're trying to mutate the collection of String s and return it with the appended string.

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

Thanks to @Aloso for help and Jussi too i managed to get the example working.

Mutation was needed in order to compile, so i ended up with code that compiles as follows:


// 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(&mut self) -> Self;
}

//TODO: Add your code here
impl AppendBar for Vec<String> {
    fn append_bar(&mut self) -> Self {
        self.push(String::from("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"));
    }
}

I believe the correct answer for this problem shall look like this:

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

impl AppendBar for Vec<String> {
    fn append_bar(mut self) -> Self {
        self.push("Bar".to_string());
        self
    }
}

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