Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Given the number of Rust developers reading this, it's probably going to be very helpful for them if you can describe what you actually got hung up on.


Another wart (in a separate post from my other wart so that replies are coherent):

I understand why &str and String are different, but why do they act like they've never heard of each other? Why do they implement such different sets of methods? Why can't they be compared for equality, so I don't always have to type "literal".to_string()?

Haskell has problems with too many string types as well (worse than Rust, because the type of their default string literals is best avoided entirely), but they fix much of the problem with the OverloadedStrings extension, which uses the type checker as something that helps you coerce string literals, instead of arguing with you.


> Why do they implement such different sets of methods?

String gets all relevant str methods from Deref; it has some additional methods, but it should largely be shared.

> Why can't they be compared for equality,

Hm? They both compare just fine with ==.


I mean, if I have a method foo() that returns a String, as many methods do, why can't I check if foo() == "bar"? Why do I have to check foo() == "bar".to_string()?


Works just fine here: https://play.rust-lang.org/?gist=5a3228a1d42d81690458337eb77...

Maybe you were running into something else?


String == &str works. Maybe you were encountering some other problem.


Oh, I guess the case of this I encountered most recently was actually Option<String> == Option<&str>.

I get why that's different, but it would be great if the type system could figure that out, so that the literal Some("foo") could be an Option<String> if necessary. Maybe I'm still being spoiled by Haskell's OverloadedStrings.


Nitpick: if you have a String and a &str, you want to convert the first to the second, not the other way around. Strings are owned, so &str to String does an allocation and copy of the bytes, while String to &str is a trivial operation that just throws away the capacity field.

But yeah, it would be nice if Rust had better ergonomics regarding coercing the insides of wrapper types. Though I'm not sure how exactly that would work.


You can do:

  foo() -> Option<String>

  foo().as_ref() == Some("str")


> Oh, I guess the case of this I encountered most recently was actually Option<String> == Option<&str>.

There are pretty rough type inference issues preventing this impl from existing. Too many people are doing `opt == None` & the compiler would no longer be able to infer the type of None. (You should not be one of them, just call `is_none()` instead)

I'd like to see this impl some day but its not clear how to make it happen.


Here's a wart. Why is reading lines from a file so hard? It's one of the first things people are going to need to do in a programming language.

On top of handling errors (which I understand is necessary, and which the ? operator makes easier), it requires importing BufReader and BufRead, and wrapping a reference to a file handle in BufReader.

Nobody is going to know how to do this unless they come across it in Rust By Example or on Stack Overflow. Shouldn't a standard library be able to abstract over fiddly details like this? Why is it my job to tell it how to buffer?

I say this as someone who is enthusiastic about Rust, but still learning.


> Why is reading lines from a file so hard?

It's not so much that it's hard, as is there's lots of options. Can you afford to read it all into memory as a String? Do you need to only read it bit by bit?

> Why is it my job to tell it how to buffer?

Systems langauges need to expose these kinds of details and levels of control.

> Nobody is going to know how to do this

I googled "rust open a file and read by lines" and got these results:

https://users.rust-lang.org/t/read-a-file-line-by-line/1585

https://doc.rust-lang.org/std/io/

https://doc.rust-lang.org/std/fs/struct.File.html

The first two show you directly how, though the first one is dealing with the error message. The last one shows reading it all in. I have some more work to do :)


It would be silly and wasteful to read the whole file into memory just so I can iterate its lines. I'm not sure why that's an option you'd need to accomodate.

However, many languages make a reasonable assumption that you can afford to fit each line in memory, and provide an obvious way to do this.


> I'm not sure why that's an option you'd need to accomodate.

Again, it's about control. Maybe you're only loading a small configuration file, and so fetching it all in one go is better than dealing with a buffer.

> an obvious way to do this.

If you search for 'line' or 'lines' in rustdoc, the correct thing is right near the top, which will show you how to use it with BufRead.


> Maybe you're only loading a small configuration file, and so fetching it all in one go is better than dealing with a buffer.

If it's small, it can't be that much worse to buffer. What's wrong with reasonable defaults?

Can you tell me what it means to search for 'line' or 'lines' in rustdoc? Is this a command-line tool? I'm aware of the 'rustdoc' that generates documentation, but not of anything by that name that searches documentation, and Googling isn't turning up much of anything except the Rust book here.

It would be great to have an offline way to search Rust documentation and examples, as things like this make it very hard to write Rust on a plane, for example.

Googling often gives answers that are wrong or pre-1.0. You seem to have better luck at searching for the right things, perhaps because you are an expert in the language and know exactly what to search for.

(Amusing related anecdote: I googled for "how to return self in rust" and got a suicide prevention website. Jeez, Google, it's not that bad.)


> Can you tell me what it means to search for 'line' or 'lines' in rustdoc?

Rustdoc has a search bar at the top. with 'line' it's the third result, 'lines' is the first https://doc.rust-lang.org/stable/std/?search=lines

and the short description makes it clear that the first two results are irrelevant in this case.

> It would be great to have an offline way to search Rust documentation and examples,

It all works offline. These docs are pre-installed for you when you install Rust, and 'cargo doc' will generate them for your whole project.

> perhaps because you are an expert in the language and know exactly what to search for.

I copy-pasted my search term exactly in the previous post.


> Rustdoc has a search bar at the top.

Let me assure you that I mean only the best for this language and I appreciate your persistent willingness to help, but I want to help debug your use of internal terminology that doesn't help beginners.

"rustdoc" is not how you should be telling beginners to look for documentation. As far as I can tell, "rustdoc" is the tool for generating documentation, not searching it. I assume you use rustdoc a lot and that's why the name comes to mind.

Maybe the term is overloaded, but I cannot find anything called "rustdoc" that involves a search bar. Googling for "rustdoc" gives many forms of Rust documentation, none of which have a search bar. DuckDuckGo-ing for "rustdoc" (as some documentation suggests instead of Googling) gets me some of the same things, plus rustdoc.com, which is a big honking security warning on top of someone's broken personal blog.

I know that exact link you gave me includes a search bar, but that's an example of finding the answer because you already know it.

And if you know to go to doc.rust-lang.org and go to the Standard Library API Reference, you get a search bar, but that's not where I would think of going to answer the question "how do I iterate lines".

That's why I end up Googling and getting to Stack Overflow, full of wrong answers and someone named Shepmaster yelling at newbies.


As I said in the other thread, I truly appreciate it. :)

Sorry, as I mentioned elsewhere I was in a meeting; I should have just waited to apply to you. It's true that "rustdoc" is overloaded, people use it to mean "rustdoc's output" which would mean the standard library docs in this case. My bad!


o/ waves

Please make sure to leave a comment and/or downvote any answers that are wrong to help people coming after you. I also apologize for whichever specific way I harmed you.


> It would be great to have an offline way to search Rust documentation and examples,

>> It all works offline. These docs are pre-installed for you when you install Rust, and 'cargo doc' will generate them for your whole project.

Not by default, anymore[0], as I found out when I wanted to read the book offline.

To set up offline documentation, do:

  rustup component add rust-docs # One-time thing
  
Now to open the docs in a browser, do one of:

  rustup doc
  rustup doc --api
  rustup doc --book
The second and third forms open up the API documentation and the book, respectively. Bookmark for one-click access.

[0] https://users.rust-lang.org/t/psa-rust-documentation-is-now-...


Gah, I always forget. I want that switched back.


> I copy-pasted my search term exactly in the previous post.

I'm bringing up a problem with how the language presents itself to beginners, not asking you to solve a specific problem for me. If searching is the answer to everything, then there needs to be a concerted effort to take Google-juice away from bad or deprecated answers.

Incidentally, the search result you're describing for "lines" says "An iterator over the lines of an instance of BufRead.", which only appears to be the answer to "how do I iterate lines of a file?" if you already know what the answer is.


No no, I very much appreciate it!

I think we have two searches confused. When I meant "my exact query, when we were taking about Google, here: https://news.ycombinator.com/item?id=13585902

I find it interesting that we got different results.

I thought "an iterator over lines" would be enough; maybe not.


That's a good point. It's slightly reminiscent of Haskell, where you have to start trying to understand the IO monad and do notation to do the most trivial real-world examples.

In other languages, you might do File.ReadAllLines("foo.txt"), and bam, you're done.


Unless foo.txt is too big, in which case, bam, out of memory.


No, Python, ruby and such have a common interface for all lazy iterations.

In Python you can just loop on anything declaring the __iter__ method, and it will lazily yield results little by little.

IO related objects implement it plus an additional layer of interface so that you can do:

    with open('file', [mode, encoding]) as f:
To get an auto closing file handle and then choose:

- for `line in f` to lazy read it line by line. This calls __iter__.

- `f.read([byte_count])` to read it all or some bunch of bytes. `f.seek(index)` to move around, etc.

- `f.readlines()` to get a list of all lines in memory.

- `f.close()` if you wish to close the file manually instead of letting the `with` keyword doing it work you.

This interface works for most IO, including files, sockets, in memory buffers, etc.

So you can choose an automatic lazy loading, a manual loading, load everything in memory, etc. And still have a lot of control.

I think rust should get some traits to expose such a common high level interface on top of the current way it deals with files, to ease simple operations. Just make sure the documentation states what you can do to go lower level.

There is a similar trend with many basic topics trying to do that as 3rd party libs too. click makes creating cmd UI very easy on top of argparse which is lower level. pendulum is higher level than datetime. requests higher than urllib. etc. Those all make doing those very common operation super easy.

Now rust is not meant to be Python/Ruby/whatever, being very low level, and checking safety at compile time implies very different requirements.

But those communities have some good concepts on API ergonomics and it would be a shame to not steal ideas from them.


The Rust team seems to not want to write trait impls where it would be inefficient to use those traits. If a method doesn't exist, its often because there is a slightly better way. They also insist that memory usage, especially any heap allocations, be very clear and explicit: allocating and attaching a buffer is probably not OK within the internal logic of the library designs.

The interface for reading is split into multiple parts. Read is the lower level interface for any stream of bytes, and is what lets you read a specific number of bytes into a preallocated buffer. Seek is implemented for files, but not all streams, because in most cases it makes no sense. There are also a number of file-specific methods directly attached to File for handling permissions and sync and such.

BufRead is the trait that lets you read all the lines, but it is explicitly not implemented for File, since using it for a File would be slow, and generally the wrong way to do things. It does have an implementation for Stdin, because that is already buffered, but if you want to associate a buffer with a file, you have to do it yourself by wrapping it in a ReadBuffer.

If I were to find a problem with the File documentation, it is that it does not mention BufReader.


Make senses. I tried to apply a solution to the wrong problem.


In C# (from which the original example is), these days - and for like 6 years now - what you do is File.ReadLines, and you get a lazy enumerator.


Iterators exist.


This was a few months ago, so I had to dig up some code: https://is.gd/kQJ7nv

Basically, I tried to create a very basic HTTP server the way I would in Go, but I kept getting all sorts of lifetime errors and kept adding stuff go get away from them. So the code has issues, lots of them. Arc<Mutex<>> was recommended by a guy at the job, I don't even know why I need an Arc here, for example.

The compiler says

    consider using an explicit lifetime parameter as shown: fn handle<'a,
    'b>(&self, req: server::Request<'a, 'b>,
    mut res: server::Response<'a>)
But when I do that I get

    error: `user_index_handler` does not live long enough
And there I'm stuck. The user_index_handler is declared and initialised literally one line above. I don't understand why Rust thinks it can just delete it immediately after I've created it.


This issue is a little bit too much in the weeds (and I'm not sure what version of Hyper you were using, it's had some big releases lately) but if you're curious, I recently implemented a basic HTTP service with a router. I still have some cleaning up to do, but https://github.com/rust-lang-nursery/thanks is the repo, and the code similar to yours is https://github.com/rust-lang-nursery/thanks/tree/master/http is the implementation, https://github.com/rust-lang-nursery/thanks/blob/master/src/... is the core of using it.

I _think_ the issue here is that Request and Response both have lifetimes. The compiler suggestion you're seeing was recently removed for having too high of a false positive rate, which I believe is what happened here. By doing that, you say that the handler must live as long as the Request and Response, which is not true, since it's created inside the functions but they're passed as arguments.


I've actually got that error with 1.15 as well. Or by "recently" you meant "on the nightly"? I updated hyper to 0.10.4, but the issue is still there.

I will check your example app out (thanks for that!) but the problem in my code still remains and I hate the fact that I can't understand what's going on. That might be my biggest issue with Rust compared to Go. Something doesn't match and I don't know how to find the issue without going to IRC or /r/rust; with Go I've never had an issue that wasn't resolved by carefully reading the language/library docs or StackOverflow.


> Or by "recently" you meant "on the nightly"?

Yeah, it was removed literally last week, so it hasn't made it into a release yet.

Without knowing what version of postgres you're using, I can't _totally_ get this to compile, it complains that SslMode isn't there. But, I did fix your issue:

  fn handle<'a, 'b>(&self, req: server::Request<'a, 'b>, mut res: server::Response<'a>)
This compiles for me.

The issue here is lifetime elision. http://rust-lang.github.io/book/ch10-03-lifetime-syntax.html...

Specifically, rule 3:

> If there are multiple input lifetime parameters, but one of them is &self or &mut self, then the lifetime of self is the lifetime assigned to all output lifetime parameters.

So here, Request and Response both have lifetime parameters. This means that your original signature is the same as

  fn handle<'a>(&'a self, req: server::Request<'a, 'a>, mut res: server::Response<'a>)
Which says "when I call handle, I borrow myself for as long as request and response are borrowed." That won't work; user_index_handler only lives for the duration of the call.

The fixed signature says

  fn handle<'a, 'b>(&self, req: server::Request<'a, 'b>, mut res: server::Response<'a>)
"When I call handle, the request and response share one lifetime, and request also has another lifetime."

You're no longer connecting &self to the request and response, and so things are just fine.

Honestly, this isn't the simplest signature; it's not surprising that you got stuck. This is also what people mean when they say "I fought with the borrow checker for a while, but then got over it", as it took me less time to fix this error than to write out this comment explaining how to! But until you've got that intuition and understanding, it can feel like hitting a brick wall.


Wow, thanks for the reply. I copied the signature directly from your post and... it didn't work. I still got `user_index_handler` does not live long enough. But! If I replace

    impl server::Handler for UserIndexHandler
with

    impl UserIndexHandler
it does compile. Removing the trait impl was another thing recommended by the Arc guy at work, he didn't know how or why it worked. Here is the code that doesn't compile with updated Postgres: https://is.gd/fjYDIG.

Which brings me to a question, how does implementing a trait prevent this code from compiling? Sorry for comparing apples to oranges again, but in Go implementing an interface is an invisible operation, that doesn't affect compilation. What's different with Rust?


I am about to go into two hours of meetings so this will have to be quick.

The issue is, because this is a trait, you must conform to its signature. Which is here: https://hyper.rs/hyper/v0.10.0/hyper/server/trait.Handler.ht... and specifically https://hyper.rs/hyper/v0.10.0/hyper/server/trait.Handler.ht...

So yeah, my idea won't work, because those types conflict.

> Removing the trait impl was another thing recommended by the Arc guy at work, he didn't know how or why it worked.

I'm surprised it works on first glance, as I was assuming that you were implementing the trait for a reason, not just because. If you don't need the trait, then yeah, it's much easier.

> What's different with Rust?

See my StackExchange link elsewhere in the thread for a summary.

I'll maybe poke at this after my meetings, or maybe tomorrow. We'll see how amped I am for coding after all that ;)


Okay, thank you again for your time!


Ah! I understand now.

Handlers aren't meant to be nested like this. That is, it's always going to break this way, because the handler needs to live as long as the request and response, so creating a sub-handler is not ever going to live long enough.

So yes, the solution is to not implement Handler for your UserIndexHandler; then you can just call it.

I bet there's something larger you could do as well, but I don't have enough experience with this interface to give good advice. Hyper's undergoing a huge docs drive for 0.11; I'm sure that'll help.


Yeah, I figured that out as well, but thank you for finding the time to analyse the code and write an answer. I appreciate it and may be on my fourth attempt at actually learning Rust :)


No problem. :)


Why not use Iron or Nickel?


They don't have support for async IO yet; this is using hyper master.


getting a bit off topic now - but I'm curious as to if async/io has improved performance by much? Do you have any benchmark comparisons?


http://aturon.github.io/blog/2016/08/11/futures/ is old but has some benchmarks in it; a lot has changed since then, but the blazing speed is still there.

I haven't benchmarked this particular application because I haven't had the time, and it's never going to see particularly higher load.


https://github.com/ninjabear/nickel-bootstrap

You might be interested in this


Why would someone trying to learn Rust be interested in a web framework? I think they ought to learn how Rust works first, or they'll just be more confused.


Different strokes for different folks




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: