
Rust traits for developer friendly libraries - nercury
http://benashford.github.io/blog/2015/05/24/rust-traits-for-developer-friendly-libraries/
======
ndarilek
I've been using Scala for years and have been eying Rust for a while. Nice to
see lots of the things I like from Scala carry over. In particular this seems
like a less magical version of Scala implicits, which seem incredibly cool at
first until you realize that a particular library or framework implements
implicits for everything, and tracking down the source for a given function
involves guessing the signature or chasing down an implicit implicit
conversion chain that gets you to one.

One thing I'm not sure about though, the article talks about implementing the
Into trait, then quickly segues over to From. When would I use Into, when
From, and do they both lead to the same end (I.e. a function that takes
Into<Whatever>?) I've looked at the docs for each, and maybe I just haven't
had enough coffee yet but the distinction isn't too clear.

~~~
steveklabnik
> When would I use Into, when From

The standard library contains this code:

    
    
        // From implies Into
        #[stable(feature = "rust1", since = "1.0.0")]
        impl<T, U> Into<U> for T where U: From<T> {
            fn into(self) -> U {
                U::from(self)
            }
        }
    

So, you basically only ever implement From, and you get the equivalent Into
for free.

More specifically, you would usually use `Into` to bound a generic function:

    
    
        fn is_hello<T: Into<Vec<u8>>>(s: T) {
            let bytes = b"hello".to_vec();
            assert_eq!(bytes, s.into());
        }
        
        let s = "hello".to_string();
        is_hello(s);
    

Whereas you'd use From to perform a conversion:

    
    
        let string = "hello".to_string();
        let other_string = String::from("hello");
        
        assert_eq!(string, other_string);
    

These APIs are pretty new; they landed _right_ before beta happened. We used
to have FromFoo traits, and this generic system replaced them.

~~~
jimmyhmiller
Maybe a bit off topic.

I've always heard that traits in rust are basically just type classes in
haskell. But I'm not quite sure how you would implement this trait as a
typeclass.

Specifically, from what I'm seeing here is that if you just implement From
you've also now implemented Into. Is there an analog in haskell? I also can't
figure out how to specify a type class that is parametrized the way into is.

Can anyone help by shedding some light on how you might go about this in
haskell? Or where traits in rust differ from typeclasses in haskell?

~~~
rapala
Standard Haskell has only single parameter type classes. With
MultiParamTypeClasses you can define

    
    
      class Into a b where
        into :: a -> b
    

Note that From would be the exact same class. I guess the distinction in Rust
is relevant because of ownership.

------
novocaine
I still have the scars from libraries implementing implicit conversions in
c++.

The question is - for people reading the code at the call site, how easy is it
to grep for what's actually happening?

I guess this rust trait at least hangs off the geobox class, but I think I
might prefer the explicit ctor for non write-only code.

~~~
steveklabnik
Coherence helps with this: you can only implement a trait for a type if you've
defined either the trait or the type. So a third party library can't impl
From<MyType> for YourType`, for example.

In general, I find Rust pretty grep-able, though I'm a bit biased.

------
jblow
This way of doing things sure sounds like it has massive performance
implications.

~~~
arthursilva
Those are very lightweight abstractions as the compiler will generate
optimized code for each variant separately. That's the entire point of Rust
Trait abstractions.

~~~
jblow
I am talking about the actual conversion of an unowned object to an owned
object, which as nearly as I can tell involves copying the object? Implicitly?
All the time?

~~~
SamReidHughes
That's no different from having a C++ function that takes a std::string, or
overloads for const std::string& and std::string&&, instead of only accepting
one. Only you have to go out of your way to have Into<String> to do it. And
we're talking about an API to make queries to send remotely.

~~~
Manishearth
Overloading doesn't scale when there are tons of ways of making a `String`. I
don't see why `Into<String>` is "out of your way"; it's actually less verbose
than overloading.

~~~
SamReidHughes
Into<String> is "out of your way" compared to having a String parameter in
Rust. (Thus the person writing the code is expressly asking for the interface
to allow implicit copying.) It is not being compared to C++ overloading. (The
point is, this is more "out of your way" than you have to go in C++, where
using an unoverloaded argument type of std::string will get you copying with
opportunistic moving, and where a const std::string& will get you copying
somewhere on the inside of the function. Thus Rust protects you from
accidental expensive copies.)

------
jdub
Hmm. _Can_ you...

    
    
      impl From<T> for GeoBox
    

... outside the module/crate in which GeoBox is defined (as suggested at the
end)?

~~~
benashford
Yes, as long as the implementation is in the same module/crate as the T.

It's only where both the left and right-hand side are in external crates that
would be a problem.

~~~
jdub
Epic, thanks!

