Traits2.rs in rustling: About traits

I know it can be me that not read document careful. But I still cannot fix the problem in this exercise:

// 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

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

////TODO: Add your code here
impl AppendBar for Vec<String> {
    fn append_bar(self) -> Vec<String> {
        self.push("Bar".to_string()) \\ *THE PROBLEM 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"));
    }
}

// In rustling notify me with error

! Compiling of exercises/traits/traits2.rs failed! Please try again. Here's the output:
error[E0308]: mismatched types
  --> exercises/traits/traits2.rs:22:9
   |
21 |     fn append_bar(self) -> Self {
   |                             ---- expected `std::vec::Vec<std::string::String>` because of return type
22 |         self.push("Bar".to_string())
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `std::vec::Vec`, found `()`
   |
   = note: expected struct `std::vec::Vec<std::string::String>`
           found unit type `()`

error: aborting due to previous error

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

Why it return () . Can someone help me.

Because Vec::push returns (). Note also that you can't return Self from append_bar(&self), unless you use clone.

upd: Not sure where did you get this trait definition, since current version on GitHub uses append_bar(self) (note the lack of ampersand), i.e. requires that append_bar consume the vector, not borrow it.

1 Like

thank you, but can you explain more clearly.

So how we can convert it back to std::vec::Vec<std::string::String>

They tell me consider what is "append 'Bar' " to a vec of String, I immediately think of use push() but now it have a problem.

push changes the original vector, so you can simply return it:

fn append_bar(self) -> Vec<String> {
    self.push("Bar".to_string());
    self
}
2 Likes

Ah, thank you very much to answer me. So your answer still not truly a solution but I still thank you because it helps me learn some things. I also think your solution is wrong, I think you should compile your solution to sure what you want to suggest before answer others. If someone so trusted in you, they can take more time just to have more trial and error to try your solution.

I will repost my solution in here:

fn main(){}

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

////TODO: Add your code here
impl AppendBar for Vec<String> {
    fn append_bar(&mut self) -> Vec<String> {
        self.push("Bar".to_string());
        self.to_vec()
        
    }
}

}

You must add &mut self to create mutable reference. Mut for having the privilege to change to value in Self and & to borrowing value.

Must return self.to_vec(), not just self, because self is a reference, not a vec<String> itself.

I was referring to the code in the repository linked above - it was using self, not &mut self. Not sure if you're supposed to change the code which is originally in place.

The mut is necessary because the Rust compiler wants to protect against accidentally mutating. Making it a reference is not necessary, and requires changing the trait. Just making the binding mutable fixes it without changing the trait:

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

(Playground)

I should note the distinction between the mut keyword as applied to a variable binding, and &mut references. When a local variable binding has mut applied to it (and this includes the arguments to a function), it signals to the compiler that you intend for the contents of the variable to be mutable, but doesn't change the type. By contrast, &mut T for a type T is the type of unique references to things of type T; &mut self at the start of an argument list is just shorthand for self : &mut Self.

1 Like

I know you don't have a choice because the AppendBar trait was given to you by the exercise, but this is a poorly defined and non-idiomatic interface.

If you are taking a borrowed self (&self or &mut self) and return an owned version (-> Self) then you'll need to make a copy at some point... But if we're going to make a copy anyway, why would you also want to take &mut self and mutate the original.

So it should either be fn append_bar(&mut self) or fn append_bar(&self) -> Self, but not a mixture of the two. Most people would prefer the first version because it lets the caller decide if they want to make a copy (e.g. by Cloneing the original first then appending "Bar" to the copy) or if they want to reuse the original.

The original formulation (fn append_bar(self) -> Self) is actually alright because we'd do our mutation and then pass back the (now mutated) original. This skips the extra copy.

3 Likes

thank you, I should learn more

Thank you for the suggestion

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.