methodsrusttraits

Is there a way to use postfix notation to call a function in Rust without defining a new trait?


Perhaps my terminology is wrong, but is there a way to use postfix notation to call a function in Rust without defining a new trait? Basically, I have a vector of &str and I'd like to convert them into a string with the notation myvec.as_string(). Currently, I can do this with the code

trait Foo {                
    fn as_string(&self) -> String;
}   

impl Foo for Vec<&str> {  
    fn as_string(&self) -> String {
        let mut mystr = self
            .iter()
            .fold(String::new(),|sum,s| format!("{}{}:", sum, s));
        mystr.pop();
        mystr
    }
}

fn main() {
    let my_vec = vec!["bar", "buz", "baz"];
    use crate::Foo;       
    println!("{}", my_vec.as_string());
}

That said, in order to make this work, I needed to define a trait called Foo that I don't really care about and the trait needed to be opened with use crate::Foo prior to the call to as_string. Is there a better way to accomplish this? And, to be clear, I'd like to avoid the notation as_string(myvec) if possible because the postfix notation has been nice for chaining together commands.


Solution

  • This is a common pattern!

    If you want to add methods to a type that is defined in another crate, the official way to do so is it define a trait and implement it for that type. If the type is from another crate then this is the only way to do it.

    A ubiquitous example of this is the crate itertools which uses a trait to add useful methods to every existing implementation of std::iter::Iterator.

    Itertools works just as you describe. There is a trait which declares a number of methods:

    pub trait Itertools : Iterator {
        fn interleave<J>(self, other: J) -> Interleave<Self, J::IntoIter>
            where J: IntoIterator<Item = Self::Item>,
                  Self: Sized
        {
            interleave(self, other)
        }
    
        // etc...
    }
    

    It is defined for all Iterators:

    impl<T: ?Sized> Itertools for T where T: Iterator { }
    

    And, whenever you want to use these extra methods, you import it:

    use itertools::Itertools;
    
    let it = (1..7).interleave(vec![-1, -2]);
    itertools::assert_equal(it, vec![1, -1, 2, -2, 3, 4, 5, 6]);