New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: `?` in `main` #1937

Merged
merged 9 commits into from Jul 17, 2017

Conversation

@zackw
Contributor

zackw commented Mar 1, 2017

Rendered.

Would resolve #1176. Pre-RFC discussion. See also #1718, #1859, and the internals thread about main returning !.

Edit: Updated render link post-merge

@SimonSapin

This comment has been minimized.

Contributor

SimonSapin commented Mar 1, 2017

I like the general idea and approach. A few comments in the details:

The boilerplate shown above will work for any E that is Display, but that is probably too general for the stdlib; we propose only Error types should work by default.

I assume this means types implementing the std::error::Error trait. I’d rather not make this trait required for main, I personally find it to be useless in practice. I don’t bother implementing it for my error types unless someone else asks for it.

Later in the RFC you propose impl<T: Termination, E: Termination> Termination for Result<T, E> which does not involve the Error trait. This seems to contradict the part quoted above.

The stdlib provides EXIT_SUCCESS and EXIT_FAILURE constants in std::process […] EXIT_FAILURE has the same value as the C library gives it, unless the C library gives it the value 1, in which case 2 is used instead.

Why? What’s wrong with EXIT_FAILURE = 1?

@Ericson2314

This comment has been minimized.

Contributor

Ericson2314 commented Mar 1, 2017

In various places, we've talked about a "needs-provides" feature for crates to declare items which are defined in a downstream crate. Ultimately, I do want this, and I want it to provide the underpinning for the waymain is handled, all the way back to crt0 and friends. [We'd still need some main-specific hacks for back compat, but hopefully just sugar.]

Anticipating this, I'd like std-less programs to stay the same, and only those linking std to benefit from this. (One can imagine std declares a new main with an impl Terminate return type, and then implements core's main with a shim calling the new main.)

It's fine if ! implements Terminate, but I don't want this to be the solution for embedded systems. On platforms where this is required (not with Terminate it would be optional), it's better for the runtime library to play the role of crt0, defining the true entry point along as a shim to call a declared main which must return !, no Terminate needed.

CC @japaric

@est31

This comment has been minimized.

Contributor

est31 commented Mar 1, 2017

👍 but it should work with the try! macro as well. It seems right now that it will be.

@zackw

This comment has been minimized.

Contributor

zackw commented Mar 2, 2017

@SimonSapin

I assume this means types implementing the std::error::Error trait. I’d rather not make this trait required for main, I personally find it to be useless in practice.

Yes, that's what I meant. i'm not wedded to it, but I hesitate to make all Display types implement Termination, because for many of them (all the numeric types, for instance) it's not obvious what the mapping from value to exit status, or even to success/failure, should be.

I suppose we could provide Termination for Result<_, E> where E: Display and map it to EXIT_FAILURE but is there a way to ensure that if E impls Termination and Display, we pick the Termination variant?

What’s wrong with EXIT_FAILURE = 1?

This is part of the stdlib staying out of the way of applications that want to implement particular Unix conventions. 1 is used by some programs to mean "no error but no matches found". The stdlib's Termination impls are all for errors, so it should avoid 1.

(This is not an essential part of the proposal but I do think it is worthwhile.)

`Termination` don't make sense in context.
There are also environments where
[returning from `main` constitutes a _bug_.][divergent-main] If you

This comment has been minimized.

@japaric

japaric Mar 2, 2017

Member

There are also environments where [returning from main constitutes a bug.]

It's totally fine to return from main (vanilla fn main()) in embedded / no_std / bare metal / microcontroller systems; I do it all the time. Just make sure you don't return from the entry point function (e.g. reset handler) because that's UB. On bare metal systems, you are in charge of everything so this is doable. I have been using this entry point for my ARM Cortex-M programs for a while now:

/// Reset handler
#[no_mangle]
pub unsafe fn _start() -> ! {
    // rustc synthesized `main` symbol. cf. `start` lang item
    extern "C" {
        fn main(argc: usize, argv: *const *const u8) -> isize;
    }

    zero_bss_section();
    initialize_data_section();

    main(0, ptr::null());

    // Go into "reactive" mode: service interrupts as they occur and sleep when there's nothing to do
    loop {
        asm!("wfi" :::: "volatile");
    }
}

So a microcontroller program like this is fine:

fn main() {
    initialize_stuff();
}

// interrupt handler
fn on_timeout() {
    LED.toggle();
}

As well as this one with a "main loop":

fn main() {
    initialize_stuff();

    loop {
        LED.toggle();
        delay_ms(500);
    }
}

This comment has been minimized.

@zackw

zackw Mar 8, 2017

Contributor

I'm not at all a microcontroller person. Divergent main is in this RFC because it was explicitly asked for over here. Are you saying that that's not actually a useful feature?

The harness for `#[test]` functions is very simple; I think it would
be enough to just give `#[test]` functions the same shim that we give
to `main`. The programmer would be responsible for adjusting their

This comment has been minimized.

@japaric

japaric Mar 2, 2017

Member

I think it would be enough to just give #[test] functions the same shim that we give to main.

This would not be compatible with no_std test runners like utest.

Supporting #[test] fn() -> Result<_, _> is actually simple. The test crate already has an enum that represents #[test] fn(), #[bench] fn(&mut Bencher), etc. We just have to add a new variant that represents the Result variant and lift the rustc restriction that functions with #[test] attribute must have signature fn(). Then it would be the job of the test runner to handle the new case.

But that would be a different RFC than this one.

This comment has been minimized.

@lucab

lucab Mar 8, 2017

I'm not sure if it is already captured anywhere, but I'd find this useful to be able to mark tests as "skipped at runtime".

This comment has been minimized.

@zackw

zackw Mar 8, 2017

Contributor

@japaric

[Giving #[test] functions the same shim as main] would not be compatible with no_std test runners like utest.

I don't follow - the whole point of the shim is to make it so external library code that expects an entry point returning () doesn't have to know or care about this RFC. Why wouldn't that work for utest?

@lucab

... to be able to mark tests as "skipped at runtime"

I want that too (e.g. it would help with #39798), and it's a very common feature in test harnesses generally, but I'm not sure how to wedge it into this RFC. I'll think about it some more, but would you mind thinking about it too?

people coming to Rust from other languages may find this _less_
surprising than the status quo.
# Alternatives

This comment has been minimized.

@japaric

japaric Mar 2, 2017

Member

Or encourage the use of templates like quickstart. That's how I start all my toy and non-toy std apps today. Granted, that wouldn't help with doctests / documentation.

This comment has been minimized.

@zackw

zackw Mar 8, 2017

Contributor

Thanks, I'll add that to the alternatives section.

if let Some(ref cause) = self.cause() {
cause.write_diagnostic(progname, stream);
}
writeln!(stream, "{}: {}\n", progname, self.description());

This comment has been minimized.

@kennytm

kennytm Mar 2, 2017

Member

That means when we use chained error the result will be reported like this, repeating the progname many times?

my_program: file not found: /foo/bar
my_program: cannot open configuration file
my_program: initialization failure

This comment has been minimized.

@zackw

zackw Mar 8, 2017

Contributor

Yes, that is what Unixy programs are expected to do. The "//unspecified but not entirely unlike:" comment is meant to give wiggle room to match the expectations of other environments.

This comment has been minimized.

@scottmcm

scottmcm Mar 9, 2017

Member

I'm not convinced Windows has an expectation for us to match. The following tools are all in %WINDIR%\System32, and some aren't even consistent with themselves:

C:\Users\Guest>findstr pgjearpgje aerg jaeiajrpoerijgaerijgaeogirje
FINDSTR: Cannot open aerg
FINDSTR: Cannot open jaeiajrpoerijgaerijgaeogirje

C:\Users\Guest>find "fawerfawef" awefa awegfawergar
File not found - AWEFA
File not found - AWEGFAWERGAR

C:\Users\Guest>where /E
ERROR: Invalid argument or option - '/E'.
Type "WHERE /?" for usage help.

C:\Users\Guest>find asdfawerfawef awefawefawefw awegfawergar
FIND: Parameter format not correct

C:\Users\Guest>convert 123
Invalid drive specification.

C:\Users\Guest>taskkill /im aergaerge
ERROR: The process "aergaerge" not found.

Makes me worry that anything we pick would be widely considered surprising.

``` rust
trait Termination {
fn write_diagnostic(&self, progname: &str, stream: &mut Write) -> ();

This comment has been minimized.

@kennytm

kennytm Mar 2, 2017

Member

std::io::Write or core::fmt::Write?

This comment has been minimized.

@zackw

zackw Mar 8, 2017

Contributor

I don't know. I was thinking std::io::Write because that's what set_panic takes, but depending on how much needs to be in libcore, it could wind up better the other one, I suppose...

We also need to decide where the trait should live. One obvious place
is in `std::process`, because that is where `exit(i32)` is; on the
other hand, this is basic enough that it may make sense to put at
least some of it in libcore.

This comment has been minimized.

@kennytm

kennytm Mar 2, 2017

Member

Termination should be put in libcore, probably as a lang-item. I don't think it is a good idea for the compiler to use a hard-coded list of valid return types, where the path std::error::Error, and worse, the OS-dependent std::process::TermStatus need to be known by the compiler.

(Also note that the current ? operator lowering does not refer to core::result::Result directly, but through the trait core::ops::Carrier.)

This comment has been minimized.

@zackw

zackw Mar 8, 2017

Contributor

What would you think of moving std::process::exit into libcore?

This comment has been minimized.

@kennytm

kennytm Mar 9, 2017

Member

@zackw The low-level entry point needs to return an i32, there is no need to use std::process::exit to return the exit code.

Most operating systems accept only a scalar exit status, but
[Plan 9][], uniquely (to my knowledge), takes an entire string (see
[`exits(2)`][exits.2]). Do we care? If we care, what do we do about
it?

This comment has been minimized.

@kennytm

kennytm Mar 2, 2017

Member

Plan 9 is not in https://forge.rust-lang.org/platform-support.html, I guess not (yet).

Plus, an integer can be easily formatted to a string.

This comment has been minimized.

@zackw

zackw Mar 8, 2017

Contributor

The point here is that hypothetically people writing Plan 9-native programs in Rust would want full control over the string passed to exits().

This comment has been minimized.

@joshtriplett

joshtriplett Jun 15, 2017

Member

I don't think that's worth complicating the interface with. People who want full control can call a platform-specific exit function.

use only `EXIT_SUCCESS` and `EXIT_FAILURE`, with one exception:
* There is a type, provisionally called `TermStatus`, which is a
newtype over `i32`; on Unix (but not on Windows), creating one from

This comment has been minimized.

@kennytm

kennytm Mar 2, 2017

Member

Windows has %ERRORLEVEL% too 😢

This comment has been minimized.

@zackw

zackw Mar 8, 2017

Contributor

I'm not sure what you are meaning to get at with this? The (but not on windows) parenthetical is because Windows does not truncate the value passed to ExitProcess, so it is correct to create TermStatus quantities from arbitrary i32's on Windows. (Technically, POSIX-compliant systems are supposed to make the full value passed to _exit available to a parent that retrieves it with waitid instead of waitpid, but I'm not aware of any actual Unix that meets that requirement.)

@aturon aturon referenced this pull request Mar 3, 2017

Open

Language ergonomic/learnability improvements #17

10 of 31 tasks complete
@scottmcm

This comment has been minimized.

Member

scottmcm commented Mar 4, 2017

I like what you said about not having i32:Termination, so TermStatus 👍 (Though I named it ExitCode in my head, because of priming.) Newtypes (or maybe #[repr(i32)] enum) FTW. Instead of panicking for out-of-range things, what about #[cfg] new methods using RFC 1868 and i32:From<TermStatus>? Would be nice to have TermStatus::Success and TermStatus::Failure too. (And, eventually, have process::exit take it, but that's another conversation.)

I love that fn main() -> ! { loop { ... } } works so naturally. Hooray for good type systems 🎉

For the trait: it feels weird to have two methods always called one after the other where one returns unit. Why not just fn handle(self, progname: &str, stream: &mut Write) -> TermStatus? (Or maybe -> Option<TermStatus>, if we want to allow abstaining from specifying one? Not sure if that's helpful...) Consuming self means that it can't accidentally run twice and thus won't accidentally write the message twice.

On progname and stream: I look at them, see that most of the impls in the RFC don't actually use them, and wonder whether they're worth it. They're making the impl have writeln!(stream, "{}: {}\n", progname, self.description()); instead of eprintln!("{}: {}\n", std::env::progname(), self.description()); (well, with RFC 1869). The progname function seems like a useful thing for error reporting, but once the method exists, the argument becomes mostly unnecessary, so I don't think either should be part of the RFC. And eprintln! seems like a better option than passing an argument that's always std::io::LOCAL_STDERR. (Not having the trait talk about things like process names or streams may also help with putting it in core...)

The two things above bring the trait down to just something like

trait Termination {
    fn report(self) -> TermStatus;
}

(I tried to give the method a name that implies its impurity, but I suspect there's a better one.)

Translating the impls from the RFC ends up something like this, which I think looks quite nice:

impl Termination for ! {
    fn report(self) -> TermStatus { unreachable!() }
}

impl Termination for () {
    fn report(self) -> TermStatus { EXIT_SUCCESS }
}

impl Termination for TermStatus {
    fn report(self) -> TermStatus { self }
}

// unspecified and private anyway, but not entirely unlike this:
fn report_recursive(error: &Error) {
    if let Some(ref cause) = error.cause() {
        cause.report_recursive());
    }
    eprintln!("{}: {}", std::env::progname(), error.description());
}

impl<E: Error> Termination for E {
    fn report(self) -> TermStatus {
        report_recursive(&self);
        TermStatus::Failure
    }
}

impl<T: Termination, E: Termination> Termination for Result<T, E> {
    fn report(self) -> TermStatus {
        match self {
            Ok(ok) => ok.report(),
            Err(err) => match err.report() {
                TermStatus::Success => TermStatus::Failure,
                e => e
            }
        }
    }
}

I'm glad you like the result owl example, but I'm still not convinced by the Result impl multiplexing back to the Termination trait. It enables a bunch of weird things, like returning io::Error instead of io::Result<()>. I like @nikomatsakis's suggestion of just having impl<E: Display> Termination for Result<(), E> that only returns TermStatus::Success or TermStatus::Failure and using custom types for anything else. Mixing cases seem fundamentally troublesome, since you have no idea if, say, Result<TermStatus, Box<Error>> might get some error that reports as the 1 that you're trying to use for vacuous-success. (I left out the non-unit success case, since I'm not convinced by it. I feel like people would expect Result<i32, E> to use the i32 as the termination status. And I'm not sure that return-from-main-as-print is something worth encouraging. I can just picture someone putting fn main() -> Result<&'static str, !> { Ok("Hello World") } in a perverse blog post...)

@nikomatsakis nikomatsakis changed the title from RFC draft: `?` in `main` to RFC: `?` in `main` Mar 4, 2017

@nikomatsakis

This comment has been minimized.

Contributor

nikomatsakis commented Mar 4, 2017

A couple of thoughts:


I think I may prefer @scottmcm's simplified traits. Ultimately, I would definitely appreciate @rust-lang/libs team feedback on "fine-tuning" the traits for ergonomics and usability, as well as the set of default impls -- this is why I tagged the RFC with T-libs.

(I think I still prefer supporting just Result<(), E>, perhaps where E: Error instead of `E: Display. The RFC text seems to suggest that this it the impl it will provide, but it actually specifies another, so I'm not sure which you had in mind, @zackw.)


The main thing I wanted to comment on, though, was the "changes to main" section. Let's start with this paragraph:

From the perspective of the code that calls main, its signature is now generic:

fn<T: Termination> main() -> T { ... }

It is critical that people writing main should not have to treat it as a generic, though. Existing code with fn main() -> () should continue to compile, and code that wants to use the new feature should be able to write fn main() -> TermT for some concrete type TermT.

I think there is some confusion here about "input" vs "output" types (also called the "universal" vs "existential", or "any" vs "some"). Specifically the T in your "generic main" example is an input type -- meaning that main doesn't get to choose what T it is invoked with. But that's not what we want. We want the main() function to specify the kind of value it returns; hence it is an "output" type. If we want to write the signature generically, then, we could write it with impl Trait, which allows us to hide the output type (but it will be inferred from the fn body):

fn main() -> impl Termination { ... }

Or of course we could write it, as we do today, with a concrete type:

fn main() { } // -> ()

If you think of it in terms of traits, there is kind of a trait like:

trait Main {
    type Output;
    fn main() -> Self::Output;
}

Continuing on to to the next paragraph, I think that looking at things in terms of "input" vs "output" types is also helpful here:

I also don't know whether the code that calls main can accept a generic. The lang_start glue currently receives an unsafe pointer to main, and expects it to have the signature () -> (). The abstractly correct new signature is () -> Termination but I suspect that this is currently not possible, pending impl Trait return types or something similar. () -> Box<Termination> probably is possible but requires a heap allocation, which is undesirable in no-std contexts.

I think the gist of @alexcrichton's comment on the internals thread (which, btw, it's probably worth linking to in the RFC), was that the actual starting point for execution is the lang_start "lang item" in the standard library, not main. Currently, that lang-item has a monomorphic signature:

fn lang_start(main: fn(), argc: isize, argv: *const *const u8) -> isize
// the actual code uses main: *const u8, but transmutes it to `fn()` later

but there is no reason it can't have a generic signature:

fn lang_start<T: Termination>(main: fn() -> T, argc: isize, argv: *const *const u8) -> isize

The key point is that the T here is an input type (unlike with main()). That is, for any given binary, there is some termination type X, which is determined by the body of the main() function, and the compiler will create a monomorphized copy of lang_start specialized to X (and exactly one, since there must be only one starting point).

@eddyb

This comment has been minimized.

Member

eddyb commented Mar 4, 2017

Heh, yeah, lang_start being monomorphic is a hold over from a time before Rust had where clauses and a real trait system, there's no real reason to keep it that way.

@kornelski

This comment has been minimized.

Contributor

kornelski commented Mar 4, 2017

I feel like there are two distinct features here:

  1. Overloaded return type
  2. Acting on main()'s returned value

The overloaded return type is handled as a bit of a fudged half-magic here. Maybe it'd be better to have a separate RFC for it?

@kornelski

This comment has been minimized.

Contributor

kornelski commented Mar 4, 2017

This RFC assumes that returning Terminate from main will be used for real programs, not just for short example code.

However, for real output from the program I need to have 100% control over how the error is formatted. For example, I need different formatting depending on program's verbose flag, and I don't want the program name, but an "error: " prefix, and I don't want to display all error causes, only some of them that don't expose implementation details, etc.

Implementation of write_diagnostic is endlessly bikesheddable. Unfortunately, customization requires wrapping errors in a newtype with Termination trait implementation, which takes more code than just having a match in main.

@scottmcm

This comment has been minimized.

Member

scottmcm commented Mar 5, 2017

Interesting, @pornel. I agree that the output format is endlessly bikesheddable. Perhaps the only way to deal with that is to re-use an existing shed: unwrap's output.

What if an Err from main still panic!ed? That's consistent with the "explicit exceptions" interpretation of Result: it gives a debug-formatted error if you let an "uncaught exception" escape main, like in many (most?) languages with exceptions. And the "just having a match in main" is perfectly analogous to putting a catch-all block to handle those exceptions if you don't want them exposed--especially if you can literally wrap it in a catch block to do so with that work now in flight. Similarly, turning an error into a panic to fail the test is perfectly reasonable, as the test runner needs to catch panics anyway. And the doc examples panicking on errors wasn't the problem, just that it happened because of syntactic unwraps. (Now, this does require considering "error returned from main" as "a bug" in order to not blatantly violate design point 3, so someone living in the hosted environment world should push back on everything I just said.)

If that's the case, then it'd bring things down to just

// Same trait, and same impls for !, (), and TermStatus

impl<T: Termination, E: Debug> Termination for Result<T, E> {
    fn report(self) -> TermStatus {
        self.expect("unhandled `Err` value").report()
    }
}

One thing I quite like about that is that it resolves my result-multiplexing-to-Termination complaints. Having fn main() -> io::Result<TermStatus> becomes reasonable, since any overlap in exit code already exists with existing panic possibilities. (And those might go away to if the RFC's "use abort instead of exit" suggestion is adopted.) And delegating on success is certainly valuable; being able to use ? inside an "infinite" loop by returning io::Result<!> is pretty cool.

now generic:
``` rust
fn<T: Termination> main() -> T { ... }

This comment has been minimized.

@mglagla

mglagla Mar 5, 2017

Angle brackets belong after the function name: fn main<T: Termination>() -> T { ... }

This comment has been minimized.

@eddyb

eddyb Mar 5, 2017

Member

I think this is pseudosyntax? Since the only way this proposal can work is fn main() -> T where T: Termination, but T has to be a concrete type.

This comment has been minimized.

@zackw

zackw Mar 8, 2017

Contributor

Yes, pseudosyntax. Also, any time you see me write something that's not quite right, please remember that I only started learning Rust this past January. (Yeah, I like jumping in at the deep end 😉)

@joshtriplett

This comment has been minimized.

Member

joshtriplett commented Mar 6, 2017

I love this proposal. This should allow short examples to stop using .unwrap() and similar, and instead use error handling that works everywhere, whether in main or deeper.

However, I would find it exceptionally confusing if Rust's EXIT_FAILURE had a different value than C's EXIT_FAILURE. Having EXIT_FAILURE map to 1 (if C does so) seems completely fine. If you really want a value that maps to something other than C's EXIT_FAILURE value, call it something else entirely. (I do like the idea of having the default exit code for the Error implementation use something other than 1; I'd just like to avoid the confusion of calling that value EXIT_FAILURE.)

@joshtriplett

This comment has been minimized.

Member

joshtriplett commented Mar 6, 2017

Also, one bikeshedding nit: TermStatus seems confusing due to abbreviating "Termination" as "Term", because in many contexts (especially that of a command-line tool) "Term" more naturally refers to "Terminal". Please consider ExitCode or similar. (See also @scottmcm's comment.)

@alexcrichton

This comment has been minimized.

Member

alexcrichton commented Mar 6, 2017

I also tend to prefer @scottmcm's simplified traits, notably consuming self and only having one method to implement rather than two separated ones (also gives the flexibility of choosing the output stream). I'll also note that a TermStatus struct would be great for cross-platform creation functions (e.g. success and failure) and it'd also be a great location to have platform-specific extension traits for the various types of return values across OSes.

I think I also agree with @nikomatsakis in that Result<T, E> is probably a little too generic for returning from main and I'd be fine with just supporting Result<(), E>.

Finally thanks so much for mentioning doctests here! I think you proposed solution will work great (and we can use crater to confirm). I wonder if perhaps we could default to -> Result<(), Box<Error>> as a type which should work for E: Error, right?

@joshtriplett

This comment has been minimized.

Member

joshtriplett commented Mar 6, 2017

@alexcrichton

I think I also agree with @nikomatsakis in that Result<T, E> is probably a little too generic for returning from main and I'd be fine with just supporting Result<(), E>.

That wouldn't work if you want to both support error-handling (using the E) and return a 0/1 exit code (using the T).

@alexcrichton

This comment has been minimized.

Member

alexcrichton commented Mar 6, 2017

Well, to be clear, literally everything is already supported today, you can just use std::process::exit. The motivation here is to use ? more liberally in main to have examples, snippets, etc, "just work" more often. Notably, the motivation is to not move as much after-main logic as possible to the return type of main. I feel like it'd be quite reasonable to say that if you want to deal with Result<bool, Error> you'd just write
.map(handle_bool_exit) and call it a day

@joshtriplett

This comment has been minimized.

Member

joshtriplett commented Mar 6, 2017

@alexcrichton std::process::exit doesn't run destructors, making it necessary to run it at a point where any destructors have already run. That often motivates the same kind of separation into a real_main() that ? does. Handling it by returning Result<ExitCode, Error> from main() seems much simpler.

@nikomatsakis

This comment has been minimized.

Contributor

nikomatsakis commented Mar 6, 2017

I think there is an interesting question as to whether this kind of return value should be something formatted "nicely enough" that for simple uses it might be acceptable to present to the user, or whether it should display something very "debug-oriented", kind of analogous to an uncaught exception in a Java program (etc). The latter is sort of what I naively expected, but I think the approach the RFC takes is more the former, and it has its appeal as well.

@joshtriplett

This comment has been minimized.

Member

joshtriplett commented Mar 6, 2017

@nikomatsakis This loses a huge amount of value if it looks like a panic! or unwrap() or expect(). I'd like something that looks appropriate to present to a user, so I can actually use it rather than having to replace it.

@rfcbot

This comment has been minimized.

rfcbot commented Jul 16, 2017

The final comment period is now complete.

1 similar comment
@rfcbot

This comment has been minimized.

rfcbot commented Jul 16, 2017

The final comment period is now complete.

@scottmcm

This comment has been minimized.

Member

scottmcm commented Jul 16, 2017

I'm glad to see this through FCP! Seems like there will be plenty to debate in implementation, though.

@aturon aturon referenced this pull request Jul 17, 2017

Open

Tracking issue for RFC 1937: `?` in `main` #43301

7 of 12 tasks complete

@aturon aturon merged commit c379f5d into rust-lang:master Jul 17, 2017

@aturon

This comment has been minimized.

Member

aturon commented Jul 17, 2017

Huzzah! This RFC has been merged! Tracking issue.

Thanks, all, for the long and vigorous discussion. This is going to be a great improvement for Rust.

@radix

This comment has been minimized.

radix commented Jul 18, 2017

can the "Rendered" link in the PR description be updated so it points to the rust-lang repo instead of the personal one (which is now 404ing)?

@scottmcm

This comment has been minimized.

Member

scottmcm commented Jul 18, 2017

@radix Updated.

perlun pushed a commit to perlun/rust that referenced this pull request Aug 11, 2017

Per Lundberg
Uncomment fn() names to make ?-examples work
I noted tonight that using the examples straight away, e.g. in your `main` method or in any other method returning a non-`std::io::Result` value fails, because of reasons mentioned in rust-lang/rfcs#1937.

I suggest uncommenting these parts of the examples, so that the examples are more "copy-pasteable" and show the true requirements for using them. The compilation errors I got was not so helpful:

`error[E0277]: the trait bound `std::string::String: std::ops::Try` is not satisfied`

(Thanks to the helpful people at #rust-beginners who helped me debug it; it was obvious once you knew the requirements for using the `?` operator.)

perlun added a commit to perlun/rust that referenced this pull request Aug 11, 2017

Uncomment fn() names to make ?-examples work
I noted tonight that using the examples straight away, e.g. in your `main` method or in any other method returning a non-`std::io::Result` value fails, because of reasons mentioned in rust-lang/rfcs#1937.

I suggest uncommenting these parts of the examples, so that the examples are more "copy-pasteable" and show the true requirements for using them. The compilation errors I got was not so helpful:

`error[E0277]: the trait bound `std::string::String: std::ops::Try` is not satisfied`

(Thanks to the helpful people at #rust-beginners who helped me debug it; it was obvious once you knew the requirements for using the `?` operator.)

perlun added a commit to perlun/rust that referenced this pull request Aug 11, 2017

I noted tonight that using the examples straight away, e.g. in your `…
…main` method or in any other method returning a non-`std::io::Result` value fails, because of reasons mentioned in rust-lang/rfcs#1937.

I suggest uncommenting these parts of the examples, so that the examples are more "copy-pasteable" and show the true requirements for using them. The compilation errors I got wasn't enough to make me realize what the problem was:

```
error[E0277]: the trait bound `std::string::String: std::ops::Try` is not satisfied
```

(Thanks to the helpful people at #rust-beginners who helped me debug it; it was obvious once you knew the prerequisites for using the `?` operator.)

@jonhoo jonhoo referenced this pull request Nov 2, 2017

Merged

Interval log support #70

8 of 8 tasks complete

@sunjay sunjay referenced this pull request Nov 10, 2017

Open

Async Main #42

@nikomatsakis

This comment has been minimized.

Contributor

nikomatsakis commented Feb 22, 2018

Question for you @rust-lang/libs folks: the Termination trait is currently at the top-level (std::Termination). It feels like it should live in a module. The RFC doesn't seem to specify, unless I missed it.

Should it be std::termination::Termination? std::process::Termination?

@withoutboats

This comment has been minimized.

Contributor

withoutboats commented Feb 22, 2018

I think process makes sense, its also where exit is.

Possibly ops, since it is sort of like operator overloading.

@Centril

This comment has been minimized.

Contributor

Centril commented Feb 22, 2018

Personally I agree with the rationale by @withoutboats, process feels apt.

I also thought of ops, but the operator overloading comes from Try and it just happens to be the case that Result<T: Termination, E: Debug> is Try as well.. Termination does not seem general enough with respect to use cases to warrant inclusion in std::ops.

@nikomatsakis

This comment has been minimized.

Contributor

nikomatsakis commented Feb 22, 2018

ok.

@ExpHP

This comment has been minimized.

ExpHP commented Feb 23, 2018

Since the built-in supported return types may be approaching stabilization, should the RFC be amended to reflect the current implementation and the reasoning behind them?

The implementation heading towards stabilization accepts E: Debug for Results, while the RFC uses E: Display.

@scottmcm scottmcm referenced this pull request Feb 24, 2018

Closed

Stabilize `main` with non-() return types #48453

0 of 2 tasks complete
@vityafx

This comment has been minimized.

vityafx commented Nov 1, 2018

Guys, what prevents us from improving support of ? in the main so that it is possible to return Option as well?

Some would mean 0 (EXIT_SUCCESS) and None would mean 1 (or EXIT_FAILURE).

@scottmcm

This comment has been minimized.

Member

scottmcm commented Nov 1, 2018

@vityafx Nothing technical, but Option<()> is rather a weird type. Do you have an example where using an option is better than just using .ok_or(something) to get a result?

@vityafx

This comment has been minimized.

vityafx commented Nov 1, 2018

Simply if I have code in the main, I need a lot of ok_or things which is ugly and inconvenient

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment