11 ms·
Rust traits for developer friendly libraries
- jblow 11y agoThis way of doing things sure sounds like it has massive performance implications.
- arthursilva 11y agoThose 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 11y agoI 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?
- MichaelGG 11y agoOnly if the user is providing borrowed strings in the first place, in which case, there's not much choice. If the user can provide an owned string, it'll just use that, no copy.
- SamReidHughes 11y agoThat'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.
- jblow 11y agoYeah, and this is one of the many reasons why performance programmers usually do not touch std::string.
- SamReidHughes 11y agoYeah, they might want some sort of string type that you can't implicitly copy, like Rust's.
- steveklabnik 11y agoRust's String does not implement Copy. &str does. (That means Strings aren't ever implicitly copied.)
- SamReidHughes 11y agoThat's what I said.
- steveklabnik 11y agoAh, now that you say that, I see that my brain produced a _very_ poor parse. Sorry :(
- SamReidHughes 11y agoI was speaking in TI-85 and you were reading in TI-83 :)
- Manishearth 11y agoOverloading 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 11y agoInto<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.)
- pcwalton 11y agoWell, it's not implicit. It's explicit both inside the function body (".into()") and in the function signature (where "into" alerts the user that something may be going on). You can also avoid the copy by just supplying String in the first place, in which case it gets moved (avoiding the allocation), not copied.
- jblow 11y agoI see. Having to say .into() makes me feel a little better about it. But it does make it clear there is a runtime performance cost to insisting on a strict ownership model.
- pcwalton 11y agoIf the API has to hang onto the string, sure. But not all APIs have to do that. The only reason why the Elasticsearch API requires owned strings in this case is that the JSON API does, and this is a trait that many JSON APIs share, regardless of language. (For example, the first Google result for "c json api" pulls up this API [1], which also copies strings.) You could write a JSON API that doesn't insist on a strict ownership model, if you wanted. There is even a type, MaybeOwned [2], in the standard library to support this kind of API. In such a library, the JSON type would have a lifetime parameter, which could be 'static for owned strings, but which could be non-static for JSON types that contain references to strings. [1]: https://jansson.readthedocs.org/en/2.7/apiref.html#string https://jansson.readthedocs.org/en/2.7/apiref.html#string [2]: http://doc.rust-lang.org/0.11.0/collections/str/type.MaybeOwned.html http://doc.rust-lang.org/0.11.0/collections/str/type.MaybeOw...
- dbaupp 11y agoIncidentally, MaybeOwned is now written `Cow<str>`. http://doc.rust-lang.org/std/borrow/enum.Cow.html http://doc.rust-lang.org/std/borrow/enum.Cow.html
- deleted 11y ago[deleted]
- 11y ago
- tatterdemalion 11y agoRust also has the AsRef<T> trait, which converts to a reference, hopefully without copying. Functions which do not need to take ownership of the object should take an AsRef<T> instead of an Into<T>.
- deleted 11y ago[deleted]
- nightpool 11y agoAs another commenter implies, trait methods are statically dispatched, so there's no indirection, and very little runtime overhead[0] [0] I'm not that great at Rust, so if there's some hidden cost to this beyond just the conversion methods themselves, someone please correct me.
- Tyr42 11y agoThere are two ways of being polymorphic over traits in Rust, using static dispatch or boxed traits. Static Dispatch is very analogous to C++ templates (with the as of yet not included in the standard "Concepts" as Traits), so you get a copy of the function specialized for each type. (We get nicer error messages than templates in C++ because we require you to declare up front what methods you are expecting the type to have at function definition time instead of checking at specialization time that everything is defined.) There is no runtime cost to using a statically dispatched trait over a hand specialized version of the function. The other method, boxed traits, is very similar to vtables. It has some runtime overhead, since the size of the time is not known, you must have a pointer to it (hence "boxed"). I think Rust currently uses fat pointers for this, that is a pair of pointers, one to the object, and the other to the vtable, since you can add new instances to types in other crates, so it'd be tricky to have a complete vtable for all methods of all the traits the type implements in one place.
- ndarilek 11y agoI'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 11y ago> 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.
- rbalicki 11y agoThank you! This is the best explanation I've been able to find for how to use From, instead of Into. One thing that's useful about From is that you can implement From<SomePublicStruct> for YourPrivateStruct, but the compiler complains about Into<YourPrivateStruct> for SomePublicStruct. SomePublicStruct could be (i32, i32) and YourPrivateStruct could be MyPoint, for example. Making YourPrivateStruct public (with pub struct) fixes things, but that's not always what you want.
- jdub 11y agoHmm. Can you... impl From<T> for GeoBox ... outside the module/crate in which GeoBox is defined (as suggested at the end)?
- benashford 11y agoYes, 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 11y agoEpic, thanks!
- untothebreach 11y agoYep, that is an advantage rust traits have over, say, java interfaces. (Though I hear java 8 is going to be able to do this as well)
- twic 11y agoJava 8 is out now and doesn't have this. I'm not aware of anything like this being on the cards for Java 9. Could you tell us more about what you heard?
- novocaine 11y agoI 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 11y agoCoherence 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.