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`.
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.
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 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:
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.
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)orfn 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.