Search code examples
stringrustvectortraits

How can I allow a Vec of custom type to be joined with &str?


I want a Vec<CustomType> to be joinable by &str. Here is what I've tried so far:

#[derive(Debug)]
struct Item {
    string: String,
}

impl Item {
    pub fn new(string: impl Into<String>) -> Self {
        Self {
            string: string.into(),
        }
    }

    pub fn to_string(&self) -> &String {
        &self.string
    }
}

impl From<&Item> for &String {
    fn from(item: &Item) -> Self {
        &item.string
    }
}

impl From<&Item> for &str {
    fn from(item: &Item) -> Self {
        &item.string.to_string()
    }
}

fn main() {
    let items = Vec::from([Item::new("Hello"), Item::new("world")]);
    let string = items.join(" ");
    println!("{}", string);
}

Which results in the error:

 $ rustc jointrait.rs 
error[E0599]: the method `join` exists for struct `Vec<Item>`, but its trait bounds were not satisfied
  --> jointrait.rs:32:24
   |
32 |     let string = items.join(" ");
   |                        ^^^^ method cannot be called on `Vec<Item>` due to unsatisfied trait bounds
   |
   = note: the following trait bounds were not satisfied:
           `[Item]: Join<_>`

The rustc help just says that some method is missing, but googling the error, I could not find out which method / trait I need to implement.


Solution

  • In order for a list of Ts to be joinable, [T] needs to implement Join<Separator>.

    If you look which things already implement Join, you will find the following entry:

    impl<S> Join<&str> for [S]
    where
        S: Borrow<str>
    

    This means, everything that implements Borrow<str> can be joined by a &str separator. So all you have to do is implement Borrow<str> for your struct:

    use std::borrow::Borrow;
    
    #[derive(Debug)]
    struct Item {
        string: String,
    }
    
    impl Item {
        pub fn new(string: impl Into<String>) -> Self {
            Self {
                string: string.into(),
            }
        }
    }
    
    impl Borrow<str> for Item {
        fn borrow(&self) -> &str {
            &self.string
        }
    }
    
    fn main() {
        let items = Vec::from([Item::new("Hello"), Item::new("world")]);
        let string = items.join(" ");
        println!("{}", string);
    }
    
    Hello world