Policy for doc examples using try!()

So. You can't use try!() in main (or any other fn that doesn't return a Result). Every experienced Rustacean knows this, but a lot of newcomers get confused because the docs have examples that look like

use std::fs::File;
let mut f = try!(File::open("foo.txt"));

while the real code (what you get if you click the invisible playpen link) is

fn main() {
    use std::fs::File;
    fn foo() -> io::Result<()> {
    let mut f = try!(File::open("foo.txt"));
    Ok(())
    }
}

I get that we want to encourage usage of try!() over, say, unwrap() even though the former requires a bit more setup, and we don't want to clutter up the examples with said setup... but hiding important parts of the code like this surely encourages people to make the try-in-main mistake. Of course, we only know how many people get confused and ask about this; we can't know how many get it right away, or understand the compile error. And there's some amount of resistance to adding enough unhidden code to the doc examples to get something where copy-paste gets you compiling code, because it adds visual noise and takes up vertical space etc etc. Hence this poll.

I see a bunch of options. This list should be considered multiple choice and incomplete.

  • Status quo. Assert that the number of people confused by this is insignificant and/or the cost of correcting their understanding once is acceptable.
  • Change the examples so that they compile without hidden lines, e.g.
use std::fs::File;
fn foo() -> io::Result<()> {
    let mut f = try!(File::open("foo.txt"));
    Ok(())
}

or

use std::fs::File;
let mut f = File::open("foo.txt").expect("could not open");
  • Make the playpen link from the doc example less invisible, especially if there are hidden lines (along the lines of "Click here to see full example").
  • Have a better compile error when a try!() macro expansion causes a return type mismatch.
  • Add a note to the Error Handling section of TRPL about why you can't use try!() from main (note: I don't think this is enough by itself, as we can't guarantee you read the whole book before looking at the stdlib docs, and you might not even know what chapter is relevant).

Thoughts?

While this is a common problem, it's only a problem once.

As such, I think ensuring there's a section of the book that explicitly spells this out (so we can link to it when the question comes up) and making it clear on the examples that they're incomplete should be sufficient.

Ideally, rustdoc would emit an extra class or something if an example contains hidden lines. That said, not every example will even run in the playpen, so maybe there should be a button to show hidden lines within example itself.

1 Like

True, but it'd be even better if it were a problem zero times because the newcomer were empowered to figure it out for themselves, either from the doc or from the compile error! :smile:

The tradeoff here is that the extra noise is worse for people who know
Rust, but better for those who don't. With the API docs, we've assumed
that you already know Rust; we don't try to make them unfriendly for
beginners, but we don't go to great lengths to explain the basics.

Showing the extra lines and/or unidiomatic code makes them more confusing,
that's the root issue.

1 Like

I'm with @DanielKeep and @steveklabnik on this one. Making sure every single example is beginner friendly at the expense of some clarity for everyone else seems like too high of a cost. It also requires more work from the person writing the example. It seems like adding a few paragraphs about this problem explicitly to the docs is a good idea. To @durka's point, it probably won't be easily discoverable to beginners, but it will at least make responding to a beginner's confusion very easy.

At some point, a beginner must learn about error handling to use Rust effectively, and in-your-face-panics because of overeager use of unwrap seems like a pretty decent forcing function to do so to me.

Maybe "in-your-face-compilations-errors" because of try! in main work better as forging functions compared to in-your-face-panics?

Compiler can detect and show a specialized friendly message about try!, Result and main.

Is there a problem apart from backward compatibility for main to return Result<(),_> that maps to zero/nonzero exit code?

there are already RFC threads/bugs for both of these things, IIRC

1 Like

Which nonzero exit code specifically? What do you do with the err value?

Could be like an uncaught C++ exception:

#include <stdexcept>
int main(void)
{
    throw std::runtime_error("foo");
}
$ g++ uncaught.cxx -o uncaught && ./uncaught
terminate called after throwing an instance of 'std::runtime_error'
  what():  foo
Aborted (core dumped)

That is, it prints the result of std::exception::what() and then calls raise(SIGABRT). (Other C++ vendors/platforms surely vary.) Rust could similarly panic!("blah blah: {:?}", err).