Readit News logoReadit News
seawlf · 7 years ago
As someone who has been programming in Rust for nearly a year, even for commercial purposes, this article is baffling to me. I've found the compiler messages to be succinct and helpful. The package system is wonderful. It's dead easy to get something off the ground quickly. All it took was learning how and when to borrow.
majewsky · 7 years ago
I can see where the author comes from. I've been working with ^W^W fighting against Tokio this week, and the error messages are horrible. Representative example:

  error[E0271]: type mismatch resolving `<futures::AndThen<futures::Select<futures::stream::ForEach<futures::stream::MapErr<std::boxed::Box<futures::Stream<Error=std::io::Error, Item=(tokio_uds::UnixStream, std::os::ext::net::SocketAddr)> + std::marker::Send>, [closure@src/server/mod.rs:59:18: 59:74]>, [closure@src/server/mod.rs:60:19: 69:10 next_connection_id:_], std::result::Result<(), ()>>, futures::MapErr<futures::Receiver<()>, [closure@src/server/mod.rs:74:18: 74:74]>>, std::result::Result<(), ((), futures::SelectNext<futures::stream::ForEach<futures::stream::MapErr<std::boxed::Box<futures::Stream<Error=std::io::Error, Item=(tokio_uds::UnixStream, std::os::ext::net::SocketAddr)> + std::marker::Send>, [closure@src/server/mod.rs:59:18: 59:74]>, [closure@src/server/mod.rs:60:19: 69:10 next_connection_id:_], std::result::Result<(), ()>>, futures::MapErr<futures::Receiver<()>, [closure@src/server/mod.rs:74:18: 74:74]>>)>, [closure@src/server/mod.rs:78:34: 83:6 cfg:_]> as futures::Future>::Error == ()`
    --> src/server/mod.rs:85:15
     |
  85 |     return Ok(Box::new(server));
     |               ^^^^^^^^^^^^^^^^ expected tuple, found ()
     |
     = note: expected type `((), futures::SelectNext<futures::stream::ForEach<futures::stream::MapErr<std::boxed::Box<futures::Stream<Error=std::io::Error, Item=(tokio_uds::UnixStream, std::os::ext::net::SocketAddr)> + std::marker::Send>, [closure@src/server/mod.rs:59:18: 59:74]>, [closure@src/server/mod.rs:60:19: 69:10 next_connection_id:_], std::result::Result<(), ()>>, futures::MapErr<futures::Receiver<()>, [closure@src/server/mod.rs:74:18: 74:74]>>)`
                found type `()`
     = note: required for the cast to the object type `futures::Future<Item=(), Error=()>`
I have hope that things will improve on this front when `impl Trait` lands.

EDIT: After re-reading this, I want to add that I don't mean to hate on Tokio. I like the basic design very much, and hope that they can work out the ergonomics issues and stabilize the API soon.

pimeys · 7 years ago
That's nothing!

      |
  212 | /     fn call(&self, payload: Self::Request) -> Self::Future {
  213 | |         let request = self.create_request(payload);
  214 | |
  215 | |         let work = async_block! {
  ...   |
  254 | |         FutureResponse(Box::new(work))
  255 | |     }
      | |_____^
  note: ...so that the type `impl futures::__rt::MyFuture<<[generator@src/client.rs:215:20: 252:10 self:&client::Client,
request:hyper::Request for<'r> {futures::Async<futures::__rt::Mu>, (), fn(std::result::Result<hyper::Response, error::Error>) -> std::result::Result<<std::result::Result<hyper::Response, error::Error> as std::ops::Try>::Ok, <std::result::Result<hyper::Response, error::Error> as std::ops::Try>::Error> {<std::result::Result<hyper::Response, error::Error> as std::ops::Try>::into_result}, futures::MapErr<hyper::client::FutureResponse, [closure@src/client.rs:217:59: 217:85]>, hyper::Response, std::option::Option<std::string::String>, &'r hyper::Response, hyper::StatusCode, fn(std::result::Result<hyper::Chunk, error::Error>) -> std::result::Result<<std::result::Result<hyper::Chunk, error::Error> as std::ops::Try>::Ok, <std::result::Result<hyper::Chunk, error::Error> as std::ops::Try>::Error> {<std::result::Result<hyper::Chunk, error::Error> as std::ops::Try>::into_result}, futures::stream::Concat2<futures::stream::MapErr<hyper::Body, [closure@src/client.rs:234:49: 234:75]>>}] as std::ops::Generator>::Return>` will meet its required lifetime bounds

kibwen · 7 years ago
Coincidentally, `impl Trait` stabilization was just approved, meaning it should be in the next beta: https://www.reddit.com/r/rust/comments/86f3h6/impl_trait_sta... . It will be a crucial step forward for Tokio's error messages.
gnode · 7 years ago
The grievances you and the article's author mention seem less to do with Rust itself, and more to do with this seemingly horrible futures library. As far as I can tell, it's still in the rust-lang-nursery, which is an indication it's not ready for prime time yet.
insulanus · 7 years ago
That is rough, but after looking at C++ template error messages a lot recently, that error message looks pretty sweet!
alexeiz · 7 years ago
Finally, a language that can compete with C++ on complexity and size of error messages!
xfer · 7 years ago
Yeah, i have recently finished a tokio based server. Working with future combinators is very frustrating. I accidentally captured a variable in a closure(should be cloned and moved), and it didn't tell me where it happened, just an error saying requires 'static lifetime for the variable.
swfsql · 7 years ago
The last tokio re-ergo really helped me and my classmate.. now we are getting stuff done, and we are enjoying it a lot, since we are combining our connections as instances of async state-machines (which, by the way, a given state may be a sub state-machine).

This is so awesome.

arghwhat · 7 years ago
I recently wrote a few projects in Rust (C/C++/Go/JavaScript/Java/Python as background), and very much like the language. My 2 cents from my endeavors with Rust

I felt like all type errors are backwards. That is, "got" was the target you are giving your type to, not the type that you are passing. This may only happen in some cases, but I just started tuning the content of those errors out and instead adjusted randomly until things worked or the message changed.

I was often getting obscure type errors that were not at all related to the issue, and sometimes the compiler just insisted that just one more burrow would do, no matter how many burrows you stack on. This is definitely because I did stupid things, but the compiler messages were only making matters worse.

String vs. str is a pain in the arse. My code was littered with .as_str() and .to_string(). I never had the right one.

Enums are super nice, but it's very annoying that you cannot just use the value as a type. My project had a lot of enums (user-provided query trees), and it was causing a lot of friction.

There are also many trivial tasks where you think "Of course there is an established ecosystem of libraries and frameworks for this", and end up proven wrong. I mostly did find one library for the thing I needed, but often immature. The HTTP server + DB game seems especially poor.

In the end, I had to quit the fun and get work done (and others did not find playing with new tools as fun as I did), so I ported the project to Go and got productive. I took a fraction of the time to write in Go, the libraries are just so much more mature, it performs significantly better than the Rust implementation (probably because of better libraries—definitely not stating that Go is faster than Rust here), compile takes 1 (one) second rather than minnutes, and there is in general just much less friction.

On the flipside, it takes about 2-3 times as much Go than Rust to do the same task, even if it was way easier to write the Go code. The code is also a lot uglier. As an especially bad case, something that was a serde macro call in the Rust version is 150 lines of manually walking maps in the Go version.

kibwen · 7 years ago
> My code was littered with .as_str() and .to_string().

PSA: If you have a variable that's a String, you can easily pass it to anything that expects a &str just by taking a reference to it:

    fn i_take_a_str(x: &str) {}
    let i_am_a_string = "foo".to_string();
    i_take_a_str(&i_am_a_string);
Every variable of type &str is just a reference to a string whose memory lives somewhere else. In the case of string literals, that memory is in the static data section of your binary. In this case, that memory is just in the original allocation of your String.

Yoric · 7 years ago
> Enums are super nice, but it's very annoying that you cannot just use the value as a type. My project had a lot of enums (user-provided query trees), and it was causing a lot of friction.

Note that OCaml has this feature of using-a-enum-value-as-a-type (or using a subset of enum values as a type, etc.). It works very well, but it quickly produces impossibly complicated error messages.

I'd like Rust to have this feature, eventually, but not before there is a good story for error messages.

arghwhat · 7 years ago
Oh, and added thing that bugged me a lot: The error part of Result<T, E>. During my short time of coding Rust (I'll get back to it later), I never really found a way to ergonomically handle errors.

I find it really awkward that the error is a concrete type, making it so that you must convert all errors to "rethrow". Go's error interface, and even exception inheritance seems to have lower friction than this.

Dead Comment

captainbland · 7 years ago
Yeah - I mean I'm only learning it as a kind of hobby at the moment but, working as a Java programmer, I find it incredible how helpful the compiler can be. It even gives you little helpful syntax hints and stuff.
dreta · 7 years ago
Writing Rust code isn’t that hard, sure. The annoyences start when you try modifying code or moving things around.

Prototyping and editing code makes for most of my work, and Rust makes that a chore. That’s my main gripe with the language. It’s more like moving through molasses than encountering a brick wall.

steveklabnik · 7 years ago
It's interesting how perspectives differ; I love refactoring Rust code more than any language I've ever used, as it catches so many of my errors when doing so for me, at compile time.
oblio · 7 years ago
Were you a C/C++ programmer before? Your starting perspective matters a lot.

Deleted Comment

tytytytytytytyt · 7 years ago
> All it took was learning how and when to borrow.

That's disingenuous. It's really not as simple as you're trying to make it sound. But I don't understand the point of this guy's blog post, unless it's just to whine. There's so little real content in his post.

geezerjay · 7 years ago
> As someone who has been programming in Rust for nearly a year, even for commercial purposes, this article is baffling to me.

The author introduces himself as having a Python background, and as someone who experienced some challenges wrapping his mind around SQL. I wouldn't expect anything different.

arghwhat · 7 years ago
I write hardware drivers (mixed kernel/user-mode) for 100Gb/s FPGA network accelerators at work in C and C++ for 3 different OS's. I'd argue that I am very much the target audience for Rust.

And I also find that SQL can be quite infuriating to deal with, and had a high-friction experience with Rust.

Dead Comment

justinpombrio · 7 years ago
> universally terrible documentation

Wait, what? Rust has what I consider the best documentation I've seen of any language. The docs explain things at a high-level, but concisely, and have numerous examples. The formatting is good, the keyboard navigation support is good, it's well-linked, and it has convenient features like links to the source and the ability to collapse everything but method headers for easier browsing. And it's extremely easy to add docs to your own project.

Maybe there are some dark corners filled with poorly documented unstable APIs that I haven't seen?

brandur · 7 years ago
(Author here.)

I wrote this piece hastily, and it really wasn't intended for this broad of an audience — I won't redact the existing wording I still think it's roughly right, but I do regret a lot of it.

Rust's docs are amazing in certain contexts — the book is great, the built-in support for documentation on types/functions/etc. is amazing, and compiling code examples are a very laudable idea.

What I'm speaking to here is more once you get into the broader ecosystem and start using a lot of non-core libraries. Oftentimes I found that the front page docs explaining the basic premise were concise and well-written, but that things got quite a bit harder when you started diving into individual classes and functions (put another way, as you started deviating from the happy path). This doesn't apply everywhere, but often the comments are very minimal and the documentation relies heavily on "types as documentation" in that there's a big list of all the traits and the functions on those traits that are implemented. In many, many cases there's little in the way of detail or examples.

I've written quite a bit of Rust now and have used many of the headliner projects. So far there have been very few crates where I didn't have to resort to eventually checking out the source tree and figuring out how to do some particular thing by examining its source code and test suite. I won't call out any single project in particular, but I found this to be the case in every one of `actix`, `clap`, `diesel`, `error-chain`, `horrorshow`, `hyper` and `juniper`, just to pick a few from the top of my `Cargo.toml` (it also happened with many other libraries). It's great that you can do this and open source is awesome, but ideally I could get by on just documentation, which is what you can do in many other languages.

Unfortunately, read with little context, it sounds like the tone of my piece was intended to crucify, but it's not. It can be simultaneously true that Rust's docs can still use lots of improvement and that the Rust team is doing an amazing job of improving them (there's just a lot of work and a long way to go). Both these facts are true with Rust.

burntsushi · 7 years ago
> (it also happened with many other libraries)

I take documentation bugs seriously. If you did this with any of my crates, please file bugs. My guess is that other crate authors might feel the same, and that they would also appreciate bug reports.

Writing good docs is super hard, because in order to do it well, one must sink themselves entirely into the perspective of someone who is seeking answers. This is hard when you already have the answers.

UncleEntity · 7 years ago
> What I'm speaking to here is more once you get into the broader ecosystem and start using a lot of non-core libraries.

Don't even know how many times I've had to dig into the python sources to figure out why something wasn't working as intended...

My favorite: I was trying to get memory buffer working for an object (following the official docs) using the C-api and it just didn't work no matter how much I fiddled with it so I go digging through the python sources and find out the fully documented feature I was attempting to use wasn't even implemented. Well, half the PEP was implemented.

I'm probably just funny that way since if I can't figure something out from docs I just go read the sources.

fafhrd91 · 7 years ago
Writing good docs is hard for sure! Especially for developers, you just want to write cool code :) but take into an account lifetime of the projects, compared to python all of them pretty young. For example I started actix-web just 5 months ago, sure it needs more documentation
wokwokwok · 7 years ago
I wonder idly if it would be useful to have 'cargo doc deps' that would generate docs for all your dependencies (or top level dependencies) in a project.

The rust docs are super useful, but not everyone bothers to publish them, and people tend to forget to pit full working examples in them.

jnordwick · 7 years ago
http://www.cplusplus.com/reference/

Far far far better documentation. The Rust documentation is a mess and difficult to read for many people, but many involved in Rust seem to deny that it is a problem.

steveklabnik · 7 years ago
Docs team lead here. Specific feedback on improving the output of the docs is absolutely, 100% welcome. Without knowing what "it" is, I can't say if we're "denying that it is a problem."

We are constantly tweaking the layout of stuff, and have some larger plans on the way as well.

plandis · 7 years ago
What is explicitly better about it? From glancing it seems similar.
dmix · 7 years ago
> The wall is impassable until a point that you make a key discovery which lets you make your through. Often that discovery is a eureka moment – it felt impossible only hours before.

These moments are my favourite, or at least most memorable, parts of learning. Which is what attracts me to hard, important concepts because they tend to be the most rewarding once you begin to grasp it.

I'll never forget when SICP's connection between programming and abstraction started to really click, after much effort, and the way it blew my mind, despite having programmed for a couple years. Really grasping the fundamental concept took the whole experience of programming to the next level - and made learning other things easier like a rolling snowball.

The fact the author's walls never got "smaller" is a real problem, that can be disheartening.

That said, brick walls aren't always bad things. Although it's often hard to tell whether it's a reflection of a poor design/structure of the thing you're learning, or just the learning material, or the learner themselves. Then there is the question of "necessary evils" of steep learning curves which, who knows, might be required entry-fee for a truly great language or tool (see: Emacs/Vim).

Haskell was much the same way for me. Each cliff I climbed had a new big cliff waiting. It got me into learning not just pure FP but also lambda calculus/set/category theory. It felt never ending. I ultimately never went "all in" with Haskell (maybe because of this) but what I did learn has been very useful in my non-Haskell programming elsewhere, so it's not all for naught. But it was admittedly a hard and significant time investment which isn't for everyone... nor necessary for being a productive programmer. But I don't regret it.

SCdF · 7 years ago
I am currently learning Rust and I feel this intensely[1].

I really want to like Rust, but I feel like the way they cope with no GC (lifetimes, borrowing) fights me at every turn, and really simple situations in other languages[2] become these intensely painful situations. Every time you think you've worked out how to fix a problem you find while you've fixed that one you've actually created 2 more.

Want to have a data structure of variable size (eg a struct with an Vector in it)? You can't do that, structs have to be fixed size. OK so I'll make it a reference to a Vector. That's great, but now you can't have a factory function because the lifetime goes out of scope. OK so I'll wrap my reference in a Box. OK that's great but now your OTHER reference: a trait (because traits are also of unknown size) is complaining. OK I'll wrap that in a Box as well. Sorry, you can't wrap this trait in a box because before your trait requires implementors to implement copy because at one point you have to use the trait in more than one place and references break the lifetimes and--- THROWS LAPTOP OUT WINDOW

I'm going to keep chipping away at it for a bit longer, but I can feel my interest waning.

[1] Except for the bad docs bit, I really like the docs, and the error messages are definitely earnest in their attempts to help you.

[2] that I'm familiar with, which are no languages that don't auto-GC

-- EDIT --

To everyone taking the first line of my intentional rant paragraph and pointing out it works fine… you're correct, I am mistaken! Let me paste the reply I made to the first person:

---

You're absolutely right. What I really meant was that you can't have Vec<T> where T is a trait without also wrapping that in a box, which for me in turn doesn't work for a bunch of other reasons.

In any case, my point remains the same: it is very challenging-- at least for someone used to just creating data structures and letting GC handle it-- to build code that does what you want, and you spend large amounts of time "fighting" the compiler. I am OK if people wish to peg that on me being stupid or whatever, it doesn't change the core point: it's hard for new people to get into, and if you're wanting there to be less Electron apps and more native apps things like Rust being easy to use seems important for that.

tkzzbneig · 7 years ago
It's important to note that in C++ you can easily run into problems with what you're describing: structs (objects) with different sizes being put into a vector. You may very well get the program to compile, but then run into odd bugs which come about because your objects get clipped to the size of the smallest possible (the base class). So any overridden methods which expect extra data in a subclass will not behave as expected.

So what you usually do here is have a pointer and a VTable and all that jazz. But there's been a resurgence in interest in putting data into contiguous blocks of memory. Check up on data driven design.

I don't think it's fair to compare GC'd languages to Rust's complexity. At least, compare C to Rust. But still, to be truly fair, weigh the usability differences against the safety differences. There are a lot of trade-offs here and perhaps this isn't the right way for you to go forward, but keep a broad view of the other aspects at play.

geezerjay · 7 years ago
> It's important to note that in C++ you can easily run into problems with what you're describing: structs (objects) with different sizes being put into a vector.

This is simply wrong.

> You may very well get the program to compile, but then run into odd bugs which come about because your objects get clipped to the size of the smallest possible (the base class).

That has nothing to do with object sizes. You're describing the object slicing problem

https://en.wikipedia.org/wiki/Object_slicing

The cause of this problem is ignorance and incompetence regarding fundamental aspects of the language.

jcelerier · 7 years ago
> So what you usually do here is have a pointer and a VTable and all that jazz. But there's been a resurgence in interest in putting data into contiguous blocks of memory. Check up on data driven design.

in particular, boost.polycollection is a nice implementation of heterogeneous vectors: http://www.boost.org/doc/libs/develop/doc/html/poly_collecti...

alexeiz · 7 years ago
> It's important to note that in C++ you can easily run into problems with what you're describing: structs (objects) with different sizes being put into a vector.

Baloney. In C++ vector<any> or vector<variant> accomplish this task without any problems whatsoever.

Dead Comment

skohan · 7 years ago
Two things jump out at me:

1. Automatic reference counting is a really good alternative to GC. It takes a little bit more book-keeping, but the performance characteristics are predictable since allocations/frees are handled along the way. Many GC implementations require execution to be halted while the reference graph is traced, which makes it a non-starter for applications trying to deliver predictable real-time performance.

2. Most of the limitations in Rust you're lamenting are there to ensure that your code is safe and performant. Rust requires adapting to very different design-patterns than you might be used to to get these benefits: if something is hard to do in Rust it's probably an anti-pattern with respect to memory performance or safety. Then again if performance isn't your main concern, maybe you don't need Rust.

pcwalton · 7 years ago
Automatic reference counting is just a slow form of GC.
nabla9 · 7 years ago
Reference counting is a form of GC.

The problem with reference counting is that it don't work with recursive structures and counters may overflow. Overflow or reaching some max value and returning runtime error are both problems.

Koshkin · 7 years ago
> Automatic reference counting is a really good alternative to GC

1) GC is lazy, whereas RC is eager;

2) the use of "pure" RC may lead to memory leaks.

Dead Comment

scrumper · 7 years ago
Minor point: you can have Vectors in structs. Vector is a fixed-size type on the stack so it works fine. And other types in structs too. EDIT: sorry, I see you got to that before I'd finished this post :)

Rust is a language where you have to kind of take a step back before working with it to read about the design tradeoffs and why they were made. Certain things you're used to doing with other languages just won't work (e.g. doubly linked lists), and trying to coerce the language into being something it's not leads to you going on one of those frustrating circular rabbit hole journeys you described so nicely above, usually ending up with laptop defenestration.

I'm a week into Rust. I find like you the compiler quite helpful, the docs comprehensive but a bit impenetrable, and the ownership & lifetime system logical on its own terms but very, very unnatural to learn. I don't actually know if its worth it yet.

I am really, really enjoying the O'Reilly Rust book ("Programming Rust"). Maybe sign up for a 10 day Safari trial (free, no card) and give the first few chapters a read. It may help reset you.

SCdF · 7 years ago
> lifetime system logical on its own terms but very, very unnatural to learn

That is a succinct explanation of it, yes! Like I can read the documentation and nod along, and the rules seem simple. Actually then remapping your brain and how you want to achieve things seems incredibly challenging

kibwen · 7 years ago
> Want to have a data structure of variable size (eg a struct with an Vector in it)? You can't do that, structs have to be fixed size.

I'm unclear what you mean here, because Vecs in Rust do have a fixed size. You can see this by using std::mem::size_of on a Vec: for any type, a Vec is three words in size. You can see this documented in the stdlib documentation for Vecs: https://doc.rust-lang.org/std/vec/struct.Vec.html#guarantees

"Vec is and always will be a (pointer, capacity, length) triplet. No more, no less."

So what you're asking for, a Vec in a struct, works just fine:

    struct Foo {
        v: Vec<i32>
    }

    let foo = Foo { v: vec![1,2,3] };
Can you elaborate on what trouble you're having?

kibwen · 7 years ago
(Responding to myself to reply to the parent's edit)

> You're absolutely right. What I really meant was that you can't have Vec<T> where T is a trait without also wrapping that in a box, which for me in turn doesn't work for a bunch of other reasons.

Ah, it sounds like you're using traits as types directly, which is very much discouraged by Rust (especially in conjunction with taking references to those traits). What the language really prefers for you to do is to use traits as bounds on generic types to get rid of the dynamic dispatch and the consequent complications with lifetimes. The only time I'd suggest using traits in the manner you've described is when you need a heterogenous collection, which isn't common in my experience. You've just so happened to stumble across one of the patterns that I most suggest beginners not to do. :P

> In any case, my point remains the same: it is very challenging-- at least for someone used to just creating data structures and letting GC handle it-- to build code that does what you want, and you spend large amounts of time "fighting" the compiler. I am OK if people wish to peg that on me being stupid or whatever, it doesn't change the core point: it's hard for new people to get into, and if you're wanting there to be less Electron apps and more native apps things like Rust being easy to use seems important for that.

I hope that nobody here's making you feel stupid, that would be pretty silly. I've been helping people learn Rust for a long time and it's indeed common for people coming from GC'd/dynamic languages to feel like things are pretty alien (to some degree attributable simply to the differences inherent to systems programming). I as well came from Java/Python and found that there were enough people in the Rust community with that same background that there's no air of elitism suggesting that one ought to feel like a moron for e.g. not knowing what a pointer is. We're all here to help each other, eh? If you ever want to give learning Rust a try again and get stuck, feel free to come ask questions on #rust at irc.mozilla.org or reddit.com/r/rust.

Jweb_Guru · 7 years ago
You can also make a struct actually variable size anyway...
dragonwriter · 7 years ago
> I really want to like Rust, but I feel like the way they cope with no GC (lifetimes, borrowing) fights me at every turn, and really simple situations in other languages[2] become these intensely painful situations.

Lifetimes/borrowing aren't for dealing with no GC, they are for dealing with a number of issues (e.g., shared-state parallelism) that GC doesn't help at all with.

> In any case, my point remains the same: it is very challenging-- at least for someone used to just creating data structures and letting GC handle it-- to build code that does what you want, and you spend large amounts of time "fighting" the compiler.

In a sense, I think that's intentional with Rust. Not unnecessary difficulty, but Rust forces a lot of complexity that would otherwise be easy to overlook and cause runtime bugs to be dealt with upfront by the developer.

For the domain Rust aims at, that probably makes writing correct code easier on balance, but it does make lots of simpler cases harder and higher-overhead than they would be in Python, or even Java, or even in some cases C++, which also isn't GCed, but still leaves a lot of what Rust bakes into static compile-time checks as runtime footguns.

physguy1123 · 7 years ago
There's nothing at all preventing you from having a Vec in a struct, and the use cases for variable sized structs are pretty rare. It sounds more like you conflated having a different problem with having a Vec be in a struct and stuff like that poisoned your further attempts to understand the language
SCdF · 7 years ago
You're absolutely right. What I really meant was that you can't have Vec<T> where T is a trait without also wrapping that in a box, which for me in turn doesn't work for a bunch of other reasons.

In any case, my point remains the same: it is very challenging-- at least for someone used to just creating data structures and letting GC handle it-- to build code that does what you want, and you spend large amounts of time "fighting" the compiler. I am OK if people wish to peg that on me being stupid or whatever, it doesn't change the core point: it's hard for new people to get into, and if you're wanting there to be less Electron apps and more native apps things like Rust being easy to use seems important for that.

oconnor663 · 7 years ago
Responding to your updated question:

> What I really meant was that you can't have Vec<T> where T is a trait without also wrapping that in a box, which for me in turn doesn't work for a bunch of other reasons.

You can use any type of pointer to refer to a trait object, not just Box. In particular, you can use &T where T is a trait. The vtable work happening at runtime is the same, but the object can live e.g. on the stack or in a vec somewhere. Of course, if you're using references, you have to convince the compiler that the object is going to stay put for as long as the reference exists, as usual for Rust. When that's not practical, usually a Box or Rc/Arc is the go-to solution. Could you tell me more about what makes Box not work for your use case?

Aside: Trait objects are one of the more complicated features of Rust, and they run into tricky limitations (like "object safety"). It's often people's first instinct to use trait object anywhere they would've used a shared base class in some other language, but that's not usually the best pattern. Using concrete types with trait bounds (`&T .. where T: MyTrait` rather than `&MyTrait`), or inventing a new enum to hold all the types you expect, or even just trying to make ordinary composition work, is usually both easier and more performant.

kibwen · 7 years ago
> You can use any type of pointer to refer to a trait object, not just Box. In particular, you can use &T where T is a trait.

You can, though due to the extra annotations required I don't suggest that people new to the language try to use trait objects with references. (Hell, I try to keep people new to the language away from trait objects entirely, they're pretty restrictive.)

gnode · 7 years ago
An example of a variable size Vec in a struct:

My understanding is that technically the Vec object itself is fixed size under the hood (a pointer and size field), but as far as I can tell, this is what you're after. It needs no references or boxes.

https://play.rust-lang.org/?gist=8d88b83daa2f389892bfa95c8db...

  extern crate rand;
  
  use rand::Rng;
  
  #[derive(Debug)]
  struct MyStruct {
      v: Vec<u32>
  }
  
  impl MyStruct {
      pub fn new() -> MyStruct {
          let mut rng = rand::thread_rng();
          let n = rng.gen_range( 4, 10 );
          MyStruct {
              v: rng.gen_iter().take( n ).collect()
          }
      }
  }
  
  fn main() {
      let ms = MyStruct::new();
      
      println!( "{:?}", ms );
  }

Deleted Comment

TheCoelacanth · 7 years ago
> What I really meant was that you can't have Vec<T> where T is a trait without also wrapping that in a box

In what language can you do something like that? In C++, it would compile but anything that you put in the vector would get sliced down to the base type. In Java, everything you put in would get boxed.

foota · 7 years ago
I feel you.

To nit though, I think the right thing to do regarding the vector is the tie the vector's lifetime to the struct. (I don't know how exactly to do that syntactically)

drbawb · 7 years ago
>Even something as simple as abstracting a new helper function often turns into a veritable odyssey because getting type annotations right can be so difficult (especially where a third party library is involved).

This is a sentiment I can't say that I share or even understand. You have a compiler doing inference, it will tell you what the types are if you ask?

A lot of times when I find myself writing something where I'm unsure of the concrete type -- I'll just stick a bogus type ascription in the expression somewhere. Then I run `rustc` knowing full well that it will fail. Somewhere in the error will be a message of the form "found <x> expected <y>" and now I know what the inferred type is.

>The horribly anti-user futures system, compiler messages generated by a misused macros that are second only to C++ template errors in how egregiously difficult they are to parse, universally terrible documentation and lacking examples, unstable APIs, type annotation hell, and so much more.

Given the author's dig at the `futures` crate though, I have a feeling a lot of the verbosity in errors they are seeing is due to the extremely long chains of combinators that the futures crate encourages. I haven't run into these errors myself since I'm avoiding async IO in rust until an `await` style abstraction becomes available. In my opinion: jumping into using an async library/runtime that is undergoing heavy development is probably not the best place to start when it comes to learning Rust.

I think this is one thing that Go definitely got right: having concurrency baked into the language that encourages a "syncrhonous-style" of programming is absolutely the way to go for approachability. I'm not sure that I'd go so far as to call `futures` user-hostile, as the tokio devs are doing great work, but it certainly isn't user-friendly yet.

majewsky · 7 years ago
> A lot of times when I find myself writing something where I'm unsure of the concrete type -- I'll just stick a bogus type ascription in the expression somewhere. Then I run `rustc` knowing full well that it will fail. Somewhere in the error will be a message of the form "found <x> expected <y>" and now I know what the inferred type is.

I've found myself doing this a lot in order to find the type of an expression. I stick it in a variable, add an `variable.asdf();` somewhere and look for the error message "no method asdf() on type <x>".

jerf · 7 years ago
Haskell has a feature called typed holes that make this official: https://wiki.haskell.org/GHC/Typed_holes

Perhaps someone should suggest that to Rust, if nobody already has. (A quick Google didn't show anything, but I didn't try very hard.) Or even implement it; there's a decent chance that's about as easy a compiler feature as someone could start with.

vkjv · 7 years ago
This is a gap I would like to be filled by an IDE. Specifically, I'd like the inference that tells me the correct type in a compile error to auto-complete a function declaration for me. It would make the experience of pulling code into a helper function much easier.
vvanders · 7 years ago
I was following along until this:

> universally terrible documentation and lacking examples, unstable APIs, type annotation hell, and so much more.

The docs in Rust are by far some of the best I've seen, without specifics it's really hard to understand what issues he hit.

b0rsuk · 7 years ago
They're completely different kind of docs that Python's. Python's are written by hand, with many examples and tips how to use the various tools. Rust's documentation very much feels generated, and the last time I checked methods were not grouped. So vast portions of a page are consumed by variants of methods (overloaded methods?) which work exactly the same except they take different argument type. Python documentation is text with methods and code samples inbetween. Rust's documentation is like someone iterated over functions and types and printed a message for each. There's A LOT of redundancy.
vvanders · 7 years ago
You mean like how Vec has a ton of examples?

https://doc.rust-lang.org/std/vec/struct.Vec.html

FWIW I'm not a huge fan of Python's docs because I can't quickly scan them to track down that one detail about a function I was using.

Rust's docs also let you collapse everything which makes browsing them much faster.

weberc2 · 7 years ago
They problem isn't that they're auto-generated, but that they're poorly organized or displayed (not sure exactly; hard to put my finger on). Everything on godoc.org (e.g., https://godoc.org/github.com/weberc2/httpeasy) is autogenerated and the readability is top notch.

By contrast, Python (which is my day-job language) is a hot mess. Usually everything is on one page, and it's often unclear which class's `__str__` method documentation you're looking at. SQLAlchemy's docs are absolutely awful in this regard. Further, links between things are poor and inconsistent (precisely because they're not autogenerated), and the dynamic nature of the language means its up to the documentation author to be explicit about the expectations (this is less a problem for well-formed functions, but for things like Pandas where every function takes a dozen combinations of arguments, it's a nightmare).

whyever · 7 years ago
I'm curious which docs you are talking about. The Python standard library has much less examples than Rust's in my experience. numpy has excellent documentation, but that's some library.

Also, I don't see the redundancy you are talking about. There is no overloading, except for traits, and for those the documentation is in one place and all the implementations are just listed, which seems pretty minimal to me.

wmonk · 7 years ago
Coming from well established js libs to python I have found the documentation really hard to deal with.

I'm not entirely sure why that is. I really like getting to a repo on github and having the docs in the readme. Both python and rust have their own language specific doc implementations ReadTheDocs and docs.rs (I think). And you usually have to go to a separate site to view them which is fine, but I really dislike ReadTheDocs. It anyways seems really hard to find what I actually want. Take flask and alembic

Yoric · 7 years ago
Side-note: I personally vastly prefer Rust's documentation to Python's, but I imagine it's a matter of taste.

So you'd suggest adding a manner of grouping methods in rustdoc?

weavie · 7 years ago
I did find the documentation for promises and tokio to be pretty confusing. But I had only been learning Rust for a few days at that point, so I think I was probably just not ready for it.

I went on to just use the basic sockets instead and that was pretty easy.

littlestymaar · 7 years ago
Tokio and futures are still in their infancy. We'll get there at some point hopefully
majewsky · 7 years ago
> I did find the documentation for promises and tokio to be pretty confusing.

I've been learning Tokio just this week, and I think this has to do with their ongoing refactoring away from the tokio-core crate to the tokio crate.

Dowwie · 7 years ago
I have been working with Rust, full time, for a little less than 100 days. Like the author, I also came from Python (I authored Yosai).

Unlike the author, I haven't been hitting brick walls. I also have observed all of the warning signs that the futures bridge on the tokio highway is unfinished so I didn't cross the barrier and still try to use it anyway.

Instead, I have been working on myriad other synchronous parts with great success. I've made substantial progress in this time largely due to the Rust community and --- the Eco system! Sure, I've had to build custom parts but there haven't been showstoppers.

pimeys · 7 years ago
I'm from the Ruby community, did a year of Scala after leaving Ruby. I'm now with rust and after the first struggle, I don't really have trouble with Rust anymore. I know the design I need to do to get certain things done, I know what is allowed now easily with purely concurrent Rust, and when do you need to split things up into threads. Right now I can refactor a project from threads to reactor in a couple of days and deploy to production with Rust. And I know everything just works.
Dowwie · 7 years ago
What type of projects?
dom96 · 7 years ago
I'm interested to know what attracts Python programmers to Rust. It's a very different language so I'm surprised to see so many python programmers take it up. What are your reasons for working with it?
Dowwie · 7 years ago
I didn't quit Python. That would be shooting myself in the foot. I just won't be using Python everywhere for everything. Programming in Python is such a pleasant experience because I can do so much with it with so little effort. Rust enables the same but in other ways. Further, I can bridge the two!
pcwalton · 7 years ago
I'm obviously about as far from the modal Rust user as one can get, but at this point the language has completely melted away into the background. I'm often tempted to write smallish scripts in dynamic languages, but even for those I frequently choose Rust just for the Cargo ecosystem. In particular I never see a reason to use C++ unless I'm contributing to a codebase that's written in it.

It takes different programmers different amounts of time to get to this point, and I certainly believe we can do better to make it easy to get up to speed, especially when async I/O is involved. But it does come eventually.

danieldk · 7 years ago
Same here. I used to use Python for small one-off scripts, but I now primarily use Rust. I can write Rust nearly as fast as Python and Go now, plus there are a lot of useful crates for the type of command-line utilities that I write.

For me the benefit is that a Rust utility usually works when it compiles, plus I can easily deploy the binaries on other machines (without going through another pyenv/pip dance).

To give an example: I work a lot with dependency treebanks on CoNLL-X format. Over my past to years with Rust, I have accumulated a bunch of utilities for doing things like paritioning data, merging treebanks, shuffling treebanks, extracting forms/lemmas, checking for cycling graphs, etc. I use them nearly daily:

https://github.com/danieldk/conllx-utils/tree/master/src/bin

sanxiyn · 7 years ago
Same here. But then I have 5 years of experience writing Rust. Wow, that feels like a totally ridiculous thing to write, but it's true.

These days I work at a startup where we are writing everything in Rust and then write C binding and Python binding to Rust code in order to connect to the outside world.

burntsushi · 7 years ago
Same here. It feels good. I've been writing lots of little utility programs in Rust lately and it has been great.
anderspitman · 7 years ago
This is heartening.