Tracking issue for `?` operator and `catch` expressions (RFC 243, `question_mark` feature) #31436

Open
nikomatsakis opened this Issue Feb 5, 2016 · 220 comments

Comments

Projects
None yet
@nikomatsakis
Contributor

nikomatsakis commented Feb 5, 2016

Tracking issue for rust-lang/rfcs#243 and rust-lang/rfcs#1859.

Implementation concerns:

  • ? operator that is roughly equivalent to try! - #31954
  • catch { ... } expression - #39849
    • resolve do catch { ... } syntax question
      • Resolved as try { .. }, - #50412
    • resolve whether catch blocks should "wrap" result value (#41414)
  • settle design of the Try trait (rust-lang/rfcs#1859)
    • implement new Try trait (in place of Carrier) and convert ? to use it (#42275)
    • add impls for Option and so forth, and a suitable family of tests (#42526)
    • improve error messages as described in the RFC (#35946)
  • reserve try in new edition
@reem

This comment has been minimized.

Show comment
Hide comment
@reem

reem Feb 5, 2016

Contributor

The accompanying RFC discusses a desugaring based on labeled return/break, are we getting that too or will there just be special treatment for ? and catch in the compiler?

EDIT: I think labeled return/break is an excellent idea separate from ? and catch, so if the answer is no I will probably open a separate RFC for it.

Contributor

reem commented Feb 5, 2016

The accompanying RFC discusses a desugaring based on labeled return/break, are we getting that too or will there just be special treatment for ? and catch in the compiler?

EDIT: I think labeled return/break is an excellent idea separate from ? and catch, so if the answer is no I will probably open a separate RFC for it.

@nikomatsakis

This comment has been minimized.

Show comment
Hide comment
@nikomatsakis

nikomatsakis Feb 5, 2016

Contributor

Labeled return/break is purely for explanatory purposes.

On Fri, Feb 5, 2016 at 3:56 PM, Jonathan Reem notifications@github.com
wrote:

The accompanying RFC discusses a desugaring based on labeled return/break,
are we getting that too or will there just be special treatment for ? and
catch in the compiler?


Reply to this email directly or view it on GitHub
#31436 (comment).

Contributor

nikomatsakis commented Feb 5, 2016

Labeled return/break is purely for explanatory purposes.

On Fri, Feb 5, 2016 at 3:56 PM, Jonathan Reem notifications@github.com
wrote:

The accompanying RFC discusses a desugaring based on labeled return/break,
are we getting that too or will there just be special treatment for ? and
catch in the compiler?


Reply to this email directly or view it on GitHub
#31436 (comment).

@glaebhoerl

This comment has been minimized.

Show comment
Hide comment
@glaebhoerl

glaebhoerl Feb 5, 2016

Contributor

Another unresolved question we have to resolve before stabilizing is what the contract which impls of Into have to obey should be -- or whether Into is even the right trait to use for the error-upcasting here. (Perhaps this should be another checklist item?)

Contributor

glaebhoerl commented Feb 5, 2016

Another unresolved question we have to resolve before stabilizing is what the contract which impls of Into have to obey should be -- or whether Into is even the right trait to use for the error-upcasting here. (Perhaps this should be another checklist item?)

@petrochenkov

This comment has been minimized.

Show comment
Hide comment
@petrochenkov

petrochenkov Feb 5, 2016

Contributor

@reem

I think labeled return/break is an excellent idea ... I will probably open a separate RFC for it.

Please do!

Contributor

petrochenkov commented Feb 5, 2016

@reem

I think labeled return/break is an excellent idea ... I will probably open a separate RFC for it.

Please do!

@thepowersgang

This comment has been minimized.

Show comment
Hide comment
@thepowersgang

thepowersgang Feb 6, 2016

Contributor

On the subject of the Carrier trait, here is a gist example of such a trait I wrote back early in the RFC process.
https://gist.github.com/thepowersgang/f0de63db1746114266d3

Contributor

thepowersgang commented Feb 6, 2016

On the subject of the Carrier trait, here is a gist example of such a trait I wrote back early in the RFC process.
https://gist.github.com/thepowersgang/f0de63db1746114266d3

@petrochenkov

This comment has been minimized.

Show comment
Hide comment
@petrochenkov

petrochenkov Feb 6, 2016

Contributor

How this is treated during parsing?

struct catch {
    a: u8
}

fn main() {

    let b = 10;
    catch { a: b } // struct literal or catch expression with type ascription inside?

}
Contributor

petrochenkov commented Feb 6, 2016

How this is treated during parsing?

struct catch {
    a: u8
}

fn main() {

    let b = 10;
    catch { a: b } // struct literal or catch expression with type ascription inside?

}
@eddyb

This comment has been minimized.

Show comment
Hide comment
@eddyb

eddyb Feb 6, 2016

Member

@petrochenkov Well, the definition couldn't affect parsing, but I think we still have a lookahead rule, based on the second token after {, : in this case, so it should still be parsed as a struct literal.

Member

eddyb commented Feb 6, 2016

@petrochenkov Well, the definition couldn't affect parsing, but I think we still have a lookahead rule, based on the second token after {, : in this case, so it should still be parsed as a struct literal.

@petrochenkov

This comment has been minimized.

Show comment
Hide comment
@petrochenkov

petrochenkov Feb 6, 2016

Contributor

Also

let c1 = catch { a: 10 };
let c2 = catch { ..c1 }; // <--

struct catch {}
let c3 = catch {}; // <--

+ rust-lang/rfcs#306 if (when!) implemented.
It seems like there are no conflicts besides struct literals.

Given the examples above I'm for the simplest solution (as usual) - always treat catch { in expression positions as start of a catch block. Nobody calls their structures catch anyway.

Contributor

petrochenkov commented Feb 6, 2016

Also

let c1 = catch { a: 10 };
let c2 = catch { ..c1 }; // <--

struct catch {}
let c3 = catch {}; // <--

+ rust-lang/rfcs#306 if (when!) implemented.
It seems like there are no conflicts besides struct literals.

Given the examples above I'm for the simplest solution (as usual) - always treat catch { in expression positions as start of a catch block. Nobody calls their structures catch anyway.

@nikomatsakis nikomatsakis removed the A-const-fn label Feb 6, 2016

@durka

This comment has been minimized.

Show comment
Hide comment
@durka

durka Feb 6, 2016

Contributor

It would be easier if a keyword was used instead of catch.

Contributor

durka commented Feb 6, 2016

It would be easier if a keyword was used instead of catch.

@bluss

This comment has been minimized.

Show comment
Hide comment
Contributor

bluss commented Feb 6, 2016

@durka

This comment has been minimized.

Show comment
Hide comment
@durka

durka Feb 6, 2016

Contributor

@bluss yeah, I admit none of them are great... override seems like the only one that is close. Or we could use do, heh. Or a combination, though I don't see any great ones immediately. do catch?

Contributor

durka commented Feb 6, 2016

@bluss yeah, I admit none of them are great... override seems like the only one that is close. Or we could use do, heh. Or a combination, though I don't see any great ones immediately. do catch?

@bluss

This comment has been minimized.

Show comment
Hide comment
@bluss

bluss Feb 6, 2016

Contributor

do is the only one that seems to be close IMO. A keyword soup with do as prefix is a bit irregular, not similar to any other part of the language? Is while let a keyword soup as well? That one feels ok now, when you are used to it.

Contributor

bluss commented Feb 6, 2016

do is the only one that seems to be close IMO. A keyword soup with do as prefix is a bit irregular, not similar to any other part of the language? Is while let a keyword soup as well? That one feels ok now, when you are used to it.

@est31

This comment has been minimized.

Show comment
Hide comment
@est31

est31 Feb 7, 2016

Contributor

port try! to use ?

Can't ? be ported to use try! instead? This would allow for the use case where you want to get a Result return path, e.g. when debugging. With try! this is fairly easy, you just override the macro at the beginning of the file (or in lib/main.rs):

macro_rules! try {
    ($expr:expr) => (match $expr {
        Result::Ok(val) => val,
        Result::Err(err) => {
            panic!("Error occured: {:?}", err)
        }
    })
}

You will get a panic stack trace starting from the first occurrence of try! in the Result return path. In fact, if you do try!(Err(sth)) if you discover an error instead of return Err(sth), you even get the full stack trace.

But when debugging foreign libraries written by people who haven't implemented that trick, one relies on try! usage somewhere higher in the chain. And now, if the library uses the ? operator with hardcoded behaviour, getting a stacktrace gets almost impossible.

It would be cool if overriding try! would affect the ? operator as well.

Later on when the macro system gets more features you can even panic! only for specific types.

If this proposal requires an RFC please let me know.

Contributor

est31 commented Feb 7, 2016

port try! to use ?

Can't ? be ported to use try! instead? This would allow for the use case where you want to get a Result return path, e.g. when debugging. With try! this is fairly easy, you just override the macro at the beginning of the file (or in lib/main.rs):

macro_rules! try {
    ($expr:expr) => (match $expr {
        Result::Ok(val) => val,
        Result::Err(err) => {
            panic!("Error occured: {:?}", err)
        }
    })
}

You will get a panic stack trace starting from the first occurrence of try! in the Result return path. In fact, if you do try!(Err(sth)) if you discover an error instead of return Err(sth), you even get the full stack trace.

But when debugging foreign libraries written by people who haven't implemented that trick, one relies on try! usage somewhere higher in the chain. And now, if the library uses the ? operator with hardcoded behaviour, getting a stacktrace gets almost impossible.

It would be cool if overriding try! would affect the ? operator as well.

Later on when the macro system gets more features you can even panic! only for specific types.

If this proposal requires an RFC please let me know.

@rpjohnst

This comment has been minimized.

Show comment
Hide comment
@rpjohnst

rpjohnst Feb 7, 2016

Contributor

Ideally ? could just be extended to provide stack trace support directly, rather than relying on the ability to override try!. Then it would work everywhere.

Contributor

rpjohnst commented Feb 7, 2016

Ideally ? could just be extended to provide stack trace support directly, rather than relying on the ability to override try!. Then it would work everywhere.

@durka

This comment has been minimized.

Show comment
Hide comment
@durka

durka Feb 7, 2016

Contributor

Stack traces are just one example (though a very useful one, seems to me).
If the Carrier trait is made to work, maybe that can cover such extensions?

On Sun, Feb 7, 2016 at 4:14 PM, Russell Johnston notifications@github.com
wrote:

Ideally ? could just be extended to provide stack trace support directly,
rather than relying on the ability to override try!. Then it would work
everywhere.


Reply to this email directly or view it on GitHub
#31436 (comment).

Contributor

durka commented Feb 7, 2016

Stack traces are just one example (though a very useful one, seems to me).
If the Carrier trait is made to work, maybe that can cover such extensions?

On Sun, Feb 7, 2016 at 4:14 PM, Russell Johnston notifications@github.com
wrote:

Ideally ? could just be extended to provide stack trace support directly,
rather than relying on the ability to override try!. Then it would work
everywhere.


Reply to this email directly or view it on GitHub
#31436 (comment).

@est31

This comment has been minimized.

Show comment
Hide comment
@est31

est31 Feb 8, 2016

Contributor

Without wanting to speculate, I think that it could work, albeit with some issues.

Consider the usual case where one has code returning some Result<V,E> value. Now we would need to allow for multiple implementations of the Carrier trait to coexist. In order to not run into E0119, one has to make all implementations out of scope (possibly through different traits which are per default not imported), and when using the ? operator, the user is required to import the wished implementation.

This would require everybody, even those who don't want to debug, to import their wished trait implementation when using ?, there would be no option for a predefined default.

Possibly E0117 can be an issue too if wanting to do custom Carrier implementations for Result<V,E>, where all types are outside bounds, so libstd should provide already provide a set of implementations of the Carrier trait with the most used use cases (trivial implementation, and panic! ing implementation, perhaps more).

Having the possibility to override via a macro would provide a greater flexibility without the additional burden on the original implementor (they don't have to import their wished implementation). But I also see that rust never had a macro based operator before, and that implementing ? via a macro isn't possible if catch { ... } is supposed to work, at least not without additional language items (return_to_catch, throw, labeled break with param as used in RFC 243).

I am okay with any setup which enables one to get Result stacktraces with an Err return path, while having only to modify a very small amount of code, prefferably at the top of the file. The solution should also work unrelated to how and where the Err type is implemented.

Contributor

est31 commented Feb 8, 2016

Without wanting to speculate, I think that it could work, albeit with some issues.

Consider the usual case where one has code returning some Result<V,E> value. Now we would need to allow for multiple implementations of the Carrier trait to coexist. In order to not run into E0119, one has to make all implementations out of scope (possibly through different traits which are per default not imported), and when using the ? operator, the user is required to import the wished implementation.

This would require everybody, even those who don't want to debug, to import their wished trait implementation when using ?, there would be no option for a predefined default.

Possibly E0117 can be an issue too if wanting to do custom Carrier implementations for Result<V,E>, where all types are outside bounds, so libstd should provide already provide a set of implementations of the Carrier trait with the most used use cases (trivial implementation, and panic! ing implementation, perhaps more).

Having the possibility to override via a macro would provide a greater flexibility without the additional burden on the original implementor (they don't have to import their wished implementation). But I also see that rust never had a macro based operator before, and that implementing ? via a macro isn't possible if catch { ... } is supposed to work, at least not without additional language items (return_to_catch, throw, labeled break with param as used in RFC 243).

I am okay with any setup which enables one to get Result stacktraces with an Err return path, while having only to modify a very small amount of code, prefferably at the top of the file. The solution should also work unrelated to how and where the Err type is implemented.

@rphmeier

This comment has been minimized.

Show comment
Hide comment
@rphmeier

rphmeier Feb 8, 2016

Contributor

Just to chime in on bikeshedding: catch in { ... } flows pretty nicely.

Contributor

rphmeier commented Feb 8, 2016

Just to chime in on bikeshedding: catch in { ... } flows pretty nicely.

@durka

This comment has been minimized.

Show comment
Hide comment
@durka

durka Feb 8, 2016

Contributor

catch! { ... } is another backcompat choice.

Contributor

durka commented Feb 8, 2016

catch! { ... } is another backcompat choice.

@durka

This comment has been minimized.

Show comment
Hide comment
@durka

durka Feb 8, 2016

Contributor

Also, not that I expect this to change anything, but a note that this is going to break multi-arm macros that were accepting $i:ident ?, in the same way that type ascription broke $i:ident : $t:ty.

Contributor

durka commented Feb 8, 2016

Also, not that I expect this to change anything, but a note that this is going to break multi-arm macros that were accepting $i:ident ?, in the same way that type ascription broke $i:ident : $t:ty.

@dgrunwald

This comment has been minimized.

Show comment
Hide comment
@dgrunwald

dgrunwald Feb 8, 2016

Contributor

Don't overdo the backwards compatibility, just treat catch as a keyword when followed by { (possibly only in expression position, but I'm not sure if that changes much compatibility-wise).

I can also imagine some possible problems that don't involve struct literals (e.g. let catch = true; if catch {}); but I prefer a minor breaking change over a more ugly syntax.

Didn't we have a for adding new keywords, anyways? We could offer some kind of from __future__ opt-in for new syntax; or specify a rust language version number on the command-line / in Cargo.toml.
I highly doubt that in the long term, we'll be able to work with only those keywords that are already reserved. We don't want our keywords to have three different meanings each, depending on context.

Contributor

dgrunwald commented Feb 8, 2016

Don't overdo the backwards compatibility, just treat catch as a keyword when followed by { (possibly only in expression position, but I'm not sure if that changes much compatibility-wise).

I can also imagine some possible problems that don't involve struct literals (e.g. let catch = true; if catch {}); but I prefer a minor breaking change over a more ugly syntax.

Didn't we have a for adding new keywords, anyways? We could offer some kind of from __future__ opt-in for new syntax; or specify a rust language version number on the command-line / in Cargo.toml.
I highly doubt that in the long term, we'll be able to work with only those keywords that are already reserved. We don't want our keywords to have three different meanings each, depending on context.

@glaebhoerl

This comment has been minimized.

Show comment
Hide comment
@glaebhoerl

glaebhoerl Feb 9, 2016

Contributor

I agree. This isn't even the first RFC where this has come up (rust-lang/rfcs#1444 is another example). I expect it won't be the last. (Also default from rust-lang/rfcs#1210, although it's not an RFC I'm in favor of.) I think we need to find a way to add honest-to-god keywords instead of trying to figure out how to ad-hoc hack the grammar for every new case.

Contributor

glaebhoerl commented Feb 9, 2016

I agree. This isn't even the first RFC where this has come up (rust-lang/rfcs#1444 is another example). I expect it won't be the last. (Also default from rust-lang/rfcs#1210, although it's not an RFC I'm in favor of.) I think we need to find a way to add honest-to-god keywords instead of trying to figure out how to ad-hoc hack the grammar for every new case.

@rkjnsn

This comment has been minimized.

Show comment
Hide comment
@rkjnsn

rkjnsn Feb 26, 2016

Contributor

Wasn't the whole argument for not reserving several keywords prior to 1.0 that we'd definitely be introducing a way to add new keywords to the language backward compatibly (possibly by explicitly opting in), so there was no point? Seems like now would be a good time.

Contributor

rkjnsn commented Feb 26, 2016

Wasn't the whole argument for not reserving several keywords prior to 1.0 that we'd definitely be introducing a way to add new keywords to the language backward compatibly (possibly by explicitly opting in), so there was no point? Seems like now would be a good time.

@aturon

This comment has been minimized.

Show comment
Hide comment
@aturon

aturon Feb 26, 2016

Member

@japaric Are you interested in reviving your old PR and taking this on?

Member

aturon commented Feb 26, 2016

@japaric Are you interested in reviving your old PR and taking this on?

@japaric

This comment has been minimized.

Show comment
Hide comment
@japaric

japaric Feb 26, 2016

Member

@aturon My implementation simply desugared foo? in the same way as try!(foo). It also only worked on method and function calls, i.e. foo.bar()? and baz()? work but quux? and (quux)? don't. Would that be okay for an initial implementation?

Member

japaric commented Feb 26, 2016

@aturon My implementation simply desugared foo? in the same way as try!(foo). It also only worked on method and function calls, i.e. foo.bar()? and baz()? work but quux? and (quux)? don't. Would that be okay for an initial implementation?

@eddyb

This comment has been minimized.

Show comment
Hide comment
@eddyb

eddyb Feb 26, 2016

Member

@japaric What was the reason for restricting it to methods and function calls? Wouldn't parsing it be easier as a general postfix operator?

Member

eddyb commented Feb 26, 2016

@japaric What was the reason for restricting it to methods and function calls? Wouldn't parsing it be easier as a general postfix operator?

@japaric

This comment has been minimized.

Show comment
Hide comment
@japaric

japaric Feb 26, 2016

Member

What was the reason for restricting it to methods and function calls?

easiest way (for me) to test the ? expansion

Wouldn't parsing it be easier as a general postfix operator?

probably

Member

japaric commented Feb 26, 2016

What was the reason for restricting it to methods and function calls?

easiest way (for me) to test the ? expansion

Wouldn't parsing it be easier as a general postfix operator?

probably

@aturon

This comment has been minimized.

Show comment
Hide comment
@aturon

aturon Feb 26, 2016

Member

@japaric It'd probably be good to generalize it to a full postfix operator (as @eddyb is suggesting), but it's fine to land ? with the simple desugaring and then add catch later.

Member

aturon commented Feb 26, 2016

@japaric It'd probably be good to generalize it to a full postfix operator (as @eddyb is suggesting), but it's fine to land ? with the simple desugaring and then add catch later.

@japaric

This comment has been minimized.

Show comment
Hide comment
@japaric

japaric Feb 26, 2016

Member

@aturon Alright, I'll look into the postfix version by next week if no one beats me to it :-).

Member

japaric commented Feb 26, 2016

@aturon Alright, I'll look into the postfix version by next week if no one beats me to it :-).

japaric added a commit to japaric/rust that referenced this issue Feb 28, 2016

implement the `?` operator
The `?` postfix operator is sugar equivalent to the try! macro, but is more amenable to chaining:
`File::open("foo")?.metadata()?.is_dir()`.

cc #31436

japaric added a commit to japaric/rust that referenced this issue Feb 28, 2016

implement the `?` operator
The `?` postfix operator is sugar equivalent to the try! macro, but is more amenable to chaining:
`File::open("foo")?.metadata()?.is_dir()`.

cc #31436

japaric added a commit to japaric/rust that referenced this issue Feb 28, 2016

implement the `?` operator
The `?` postfix operator is sugar equivalent to the try! macro, but is more amenable to chaining:
`File::open("foo")?.metadata()?.is_dir()`.

`?` is accepted on any *expression* that can return a `Result`, e.g. `x()?`, `y!()?`, `{z}?`,
`(w)?`, etc. And binds more tightly than unary operators, e.g. `!x?` is parsed as `!(x?)`.

cc #31436
@japaric

This comment has been minimized.

Show comment
Hide comment
@japaric

japaric Feb 28, 2016

Member

rebased/updated my PR in #31954 :-)

Member

japaric commented Feb 28, 2016

rebased/updated my PR in #31954 :-)

@est31

This comment has been minimized.

Show comment
Hide comment
@est31

est31 Feb 29, 2016

Contributor

What about support for providing stack traces? Is that planned?

Contributor

est31 commented Feb 29, 2016

What about support for providing stack traces? Is that planned?

@hexsel

This comment has been minimized.

Show comment
Hide comment
@hexsel

hexsel Feb 29, 2016

I hate to be the +1 guy, but stack traces have saved good chunks of my time in the past. Maybe on debug builds, and when hitting the error path, the ? operator could append the file/line to a Vec in Result? Maybe the Vec could be debug-only too?

And that could be either exposed or turned into part of the error description...

hexsel commented Feb 29, 2016

I hate to be the +1 guy, but stack traces have saved good chunks of my time in the past. Maybe on debug builds, and when hitting the error path, the ? operator could append the file/line to a Vec in Result? Maybe the Vec could be debug-only too?

And that could be either exposed or turned into part of the error description...

@cramertj

This comment has been minimized.

Show comment
Hide comment
@cramertj

cramertj May 30, 2017

Member

@withoutboats Yes, it's currently do catch, since catch { ... } conflicts with struct literals (struct catch { }; catch { }).
.
@archshift As @SimonSapin pointed out above, closures interfere with break, continue, and return.

Member

cramertj commented May 30, 2017

@withoutboats Yes, it's currently do catch, since catch { ... } conflicts with struct literals (struct catch { }; catch { }).
.
@archshift As @SimonSapin pointed out above, closures interfere with break, continue, and return.

@aturon

This comment has been minimized.

Show comment
Hide comment
@aturon

aturon May 30, 2017

Member

@bstrie I find that I pretty frequently want catch these days; it comes up a lot during refactoring.

Member

aturon commented May 30, 2017

@bstrie I find that I pretty frequently want catch these days; it comes up a lot during refactoring.

@bstrie

This comment has been minimized.

Show comment
Hide comment
@bstrie

bstrie May 31, 2017

Contributor

I didn't realize that we were planning on requiring do catch as the syntax. Given that the risk of real-world breakage seems exceedingly low (both violates the struct naming guidelines and would have to have the constructor be the first expression in the statement (which is rare outside of return position)), could we perhaps leverage rustfmt to rewrite any offending identifiers to catch_? If it requires Rust 2.0 to do so, then, well, I've always been one to say that Rust 2.0 ought to contain only trivial breaking changes anyway... :P

Contributor

bstrie commented May 31, 2017

I didn't realize that we were planning on requiring do catch as the syntax. Given that the risk of real-world breakage seems exceedingly low (both violates the struct naming guidelines and would have to have the constructor be the first expression in the statement (which is rare outside of return position)), could we perhaps leverage rustfmt to rewrite any offending identifiers to catch_? If it requires Rust 2.0 to do so, then, well, I've always been one to say that Rust 2.0 ought to contain only trivial breaking changes anyway... :P

@withoutboats

This comment has been minimized.

Show comment
Hide comment
@withoutboats

withoutboats May 31, 2017

Contributor

@bstrie We absolutely don't want to require do catch long term. The discussion that led to using that syntax for now is here: #39921

Contributor

withoutboats commented May 31, 2017

@bstrie We absolutely don't want to require do catch long term. The discussion that led to using that syntax for now is here: #39921

@bstrie

This comment has been minimized.

Show comment
Hide comment
@bstrie

bstrie May 31, 2017

Contributor

Excellent, thanks for the context.

Contributor

bstrie commented May 31, 2017

Excellent, thanks for the context.

@RalfJung

This comment has been minimized.

Show comment
Hide comment
@RalfJung

RalfJung May 31, 2017

Member

I just came here because I hoped catch was stable, and had to learn it is not -- so yes, absolutely, now that ? is stable, it'd be great to also have catch.

Member

RalfJung commented May 31, 2017

I just came here because I hoped catch was stable, and had to learn it is not -- so yes, absolutely, now that ? is stable, it'd be great to also have catch.

@camlorn

This comment has been minimized.

Show comment
Hide comment
@camlorn

camlorn Jun 9, 2017

Contributor

I wanted to see how far we'd gotten on the rest of this and saw the do catch discussion.

I've been considering a probably silly idea that would help in cases like this: allow optionally prefixing keywords with @ or some other sigil, then make all new keywords use the sigil only. We also had a similar problem with the coroutine discussion. I can't remember if I went into this as a solution there--I might have--but it looks like this may keep coming up.

Contributor

camlorn commented Jun 9, 2017

I wanted to see how far we'd gotten on the rest of this and saw the do catch discussion.

I've been considering a probably silly idea that would help in cases like this: allow optionally prefixing keywords with @ or some other sigil, then make all new keywords use the sigil only. We also had a similar problem with the coroutine discussion. I can't remember if I went into this as a solution there--I might have--but it looks like this may keep coming up.

@scottmcm

This comment has been minimized.

Show comment
Hide comment
@scottmcm

scottmcm Jun 9, 2017

Member

FWIW, C# actually supports the opposite: @ for using keywords as identifiers. That's commonly seen in Razor, where you pass things like new { @class = "errorbox" } to set properties on HTML nodes.

Member

scottmcm commented Jun 9, 2017

FWIW, C# actually supports the opposite: @ for using keywords as identifiers. That's commonly seen in Razor, where you pass things like new { @class = "errorbox" } to set properties on HTML nodes.

@camlorn

This comment has been minimized.

Show comment
Hide comment
@camlorn

camlorn Jun 9, 2017

Contributor

@scottmcm
That's interesting. I didn't know about it. But for Rust we have to go the other way because compatibility.

Neat idea on their part, though. The .net ecosystem has a lot of languages, all with disparate keywords and all able to call each other.

Contributor

camlorn commented Jun 9, 2017

@scottmcm
That's interesting. I didn't know about it. But for Rust we have to go the other way because compatibility.

Neat idea on their part, though. The .net ecosystem has a lot of languages, all with disparate keywords and all able to call each other.

@camlorn

This comment has been minimized.

Show comment
Hide comment
@camlorn

camlorn Jun 14, 2017

Contributor

One other possibility for the future of keywords: put them behind an attribute, like some sort of stable #[feature].

I think this problem will need a more general solution in the long run.

Contributor

camlorn commented Jun 14, 2017

One other possibility for the future of keywords: put them behind an attribute, like some sort of stable #[feature].

I think this problem will need a more general solution in the long run.

@jimmycuadra

This comment has been minimized.

Show comment
Hide comment
@jimmycuadra

jimmycuadra Jun 14, 2017

Contributor

@camlorn This thread may interest you, specifically Aaron's idea about Rust "epochs": https://internals.rust-lang.org/t/pre-rfc-stable-features-for-breaking-changes/5002

Contributor

jimmycuadra commented Jun 14, 2017

@camlorn This thread may interest you, specifically Aaron's idea about Rust "epochs": https://internals.rust-lang.org/t/pre-rfc-stable-features-for-breaking-changes/5002

@glaebhoerl

This comment has been minimized.

Show comment
Hide comment
@glaebhoerl

glaebhoerl Jun 28, 2017

Contributor

I think we should link #42327 from the issue description here. (Also maybe the RFC text should be updated to link there instead of here.)

(EDIT: I posted a comment there, not sure who is or isn't already subscribed to it!)

Contributor

glaebhoerl commented Jun 28, 2017

I think we should link #42327 from the issue description here. (Also maybe the RFC text should be updated to link there instead of here.)

(EDIT: I posted a comment there, not sure who is or isn't already subscribed to it!)

@scottmcm

This comment has been minimized.

Show comment
Hide comment
@scottmcm

scottmcm Jun 29, 2017

Member

@camlorn It could, however, open up the "rustfmt does a trivial rewrite for a while, then it becomes a keyword" path. AKA take advantage of the "it's not breaking if there's an extra annotation that could have been written that would make it work in both" escape hatch in the stability guarantee. And, similar to UFCS for inference changes, a hypothetical "store in fully-elaborated form" model could keep old crates working.

Member

scottmcm commented Jun 29, 2017

@camlorn It could, however, open up the "rustfmt does a trivial rewrite for a while, then it becomes a keyword" path. AKA take advantage of the "it's not breaking if there's an extra annotation that could have been written that would make it work in both" escape hatch in the stability guarantee. And, similar to UFCS for inference changes, a hypothetical "store in fully-elaborated form" model could keep old crates working.

@phaux

This comment has been minimized.

Show comment
Hide comment
@phaux

phaux Sep 13, 2017

I wouldn't mind if the syntax for catch block was just do { … } or even ?{ … }

phaux commented Sep 13, 2017

I wouldn't mind if the syntax for catch block was just do { … } or even ?{ … }

@rpjohnst

This comment has been minimized.

Show comment
Hide comment
@rpjohnst

rpjohnst Sep 13, 2017

Contributor

do has the nice property of already being a keyword. It has the dubious (depending on your perspective) property of invoking Haskell-like do-notation, though that never stopped its past uses, and this one is somewhat closer in use case.

Contributor

rpjohnst commented Sep 13, 2017

do has the nice property of already being a keyword. It has the dubious (depending on your perspective) property of invoking Haskell-like do-notation, though that never stopped its past uses, and this one is somewhat closer in use case.

@hoodie

This comment has been minimized.

Show comment
Hide comment
@hoodie

hoodie Sep 13, 2017

Contributor

it also looks like, but behaves different than javascripts proposed do expressions

Contributor

hoodie commented Sep 13, 2017

it also looks like, but behaves different than javascripts proposed do expressions

@rpjohnst

This comment has been minimized.

Show comment
Hide comment
@rpjohnst

rpjohnst Sep 13, 2017

Contributor

That proposal isn't really relevant to Rust, which already uses bare blocks as expressions.

Contributor

rpjohnst commented Sep 13, 2017

That proposal isn't really relevant to Rust, which already uses bare blocks as expressions.

@hoodie

This comment has been minimized.

Show comment
Hide comment
@hoodie

hoodie Sep 14, 2017

Contributor

I was just pointing out the possible confusion since both would look the same but do entirely different things.

Contributor

hoodie commented Sep 14, 2017

I was just pointing out the possible confusion since both would look the same but do entirely different things.

@phaux

This comment has been minimized.

Show comment
Hide comment
@phaux

phaux Sep 14, 2017

It has the dubious (depending on your perspective) property of invoking Haskell-like do-notation

@rpjohnst Result and Option are monads, so it's at least conceptually similar. It should also be forward-compatible to extend it in the future to support all the monads.

phaux commented Sep 14, 2017

It has the dubious (depending on your perspective) property of invoking Haskell-like do-notation

@rpjohnst Result and Option are monads, so it's at least conceptually similar. It should also be forward-compatible to extend it in the future to support all the monads.

@rpjohnst

This comment has been minimized.

Show comment
Hide comment
@rpjohnst

rpjohnst Sep 14, 2017

Contributor

Right, in the past the objection has been that if we add do for something conceptually similar to monads, but without fully supporting them, it would make people sad.

At the same time, while we could probably make it forward-compatible with full do-notation, we probably shouldn't add full do-notation. It is not composable with control structures like if/while/for/loop or break/continue/return, which we must be able to use inside and across catch (or in this case do) blocks. (This is because full do-notation is defined in terms of higher order functions, and if we stuff the contents of a catch block into a series of nested closures, suddenly control flow all breaks.)

So in the end this downside of do is that it looks like do-notation without actually being do-notation, and without a good path forward to becoming do-notation either. Personally, I'm totally fine with this because Rust isn't gonna get do notation anyway- but that's the confusion.

Contributor

rpjohnst commented Sep 14, 2017

Right, in the past the objection has been that if we add do for something conceptually similar to monads, but without fully supporting them, it would make people sad.

At the same time, while we could probably make it forward-compatible with full do-notation, we probably shouldn't add full do-notation. It is not composable with control structures like if/while/for/loop or break/continue/return, which we must be able to use inside and across catch (or in this case do) blocks. (This is because full do-notation is defined in terms of higher order functions, and if we stuff the contents of a catch block into a series of nested closures, suddenly control flow all breaks.)

So in the end this downside of do is that it looks like do-notation without actually being do-notation, and without a good path forward to becoming do-notation either. Personally, I'm totally fine with this because Rust isn't gonna get do notation anyway- but that's the confusion.

bors added a commit that referenced this issue Sep 29, 2017

Auto merge of #42526 - huntiep:try_opt, r=nikomatsakis
Impl Try for Option

This is part of #31436.
@crypto-universe

This comment has been minimized.

Show comment
Hide comment
@crypto-universe

crypto-universe Oct 14, 2017

Contributor

@nikomatsakis , #42526 is merged, you can mark it as done in tracking list :)

Contributor

crypto-universe commented Oct 14, 2017

@nikomatsakis , #42526 is merged, you can mark it as done in tracking list :)

@parkovski

This comment has been minimized.

Show comment
Hide comment
@parkovski

parkovski Oct 14, 2017

Is it possible to make the catch keyword contextual but disable it if a struct catch is in scope, and issue a deprecation warning?

Is it possible to make the catch keyword contextual but disable it if a struct catch is in scope, and issue a deprecation warning?

@Emilgardis Emilgardis referenced this issue in rust-lang-nursery/failure Nov 28, 2017

Closed

impl Fail for NoneError? #59

@alercah alercah referenced this issue in rust-lang/rfcs Jan 17, 2018

Merged

RFC: label-break-value #2046

@Definitely13OrOlderISwear

This comment has been minimized.

Show comment
Hide comment
@Definitely13OrOlderISwear

Definitely13OrOlderISwear Jan 21, 2018

Not sure how appropriate this is but I ran into an issue which maybe this needs to solve in that sometimes you want to abort an ok rather than error value which is currently possible when you use return insofar that you often want to abort an inner error through an outer okay. Like when a function say returns Option<Result<_,_>> which is quite common from say an iterator.

In particular I have a macro in a project which I use copiously right now:

macro-rules! option_try {
	( $expr:expr ) => {
		match $expr {
			Ok(x)  => x,
			Err(e) => return Some(Err(e.into())),
		}
	}
}

It's very common that a function called within an Iterator::next implementation needs to immediately abort with Some(Err(e)) on failure. This macro works inside of a normal function body but not inside of a catch block because catch doesn't grab return categorically but just the special ? syntax.

Though in the end labeled-returns would make the entire catch block idea redundant wouldn't they?

Definitely13OrOlderISwear commented Jan 21, 2018

Not sure how appropriate this is but I ran into an issue which maybe this needs to solve in that sometimes you want to abort an ok rather than error value which is currently possible when you use return insofar that you often want to abort an inner error through an outer okay. Like when a function say returns Option<Result<_,_>> which is quite common from say an iterator.

In particular I have a macro in a project which I use copiously right now:

macro-rules! option_try {
	( $expr:expr ) => {
		match $expr {
			Ok(x)  => x,
			Err(e) => return Some(Err(e.into())),
		}
	}
}

It's very common that a function called within an Iterator::next implementation needs to immediately abort with Some(Err(e)) on failure. This macro works inside of a normal function body but not inside of a catch block because catch doesn't grab return categorically but just the special ? syntax.

Though in the end labeled-returns would make the entire catch block idea redundant wouldn't they?

@mark-i-m

This comment has been minimized.

Show comment
Hide comment
@mark-i-m

mark-i-m Apr 29, 2018

Contributor

It looks like #41414 is done. Could someone update the OP?

Contributor

mark-i-m commented Apr 29, 2018

It looks like #41414 is done. Could someone update the OP?

@Centril

This comment has been minimized.

Show comment
Hide comment
@Centril

Centril May 3, 2018

Contributor

Update: RFC rust-lang/rfcs#2388 is now merged and so catch { .. } is to be replaced with try { .. }.
See the tracking issue right above this comment.

Contributor

Centril commented May 3, 2018

Update: RFC rust-lang/rfcs#2388 is now merged and so catch { .. } is to be replaced with try { .. }.
See the tracking issue right above this comment.

@Nemo157

This comment has been minimized.

Show comment
Hide comment
@Nemo157

Nemo157 May 3, 2018

Contributor

What does this mean for Edition 2015, is the do catch { .. } syntax still on a path towards stabilization, or will this be dropped and only supported via try { .. } in Edition 2018+?

Contributor

Nemo157 commented May 3, 2018

What does this mean for Edition 2015, is the do catch { .. } syntax still on a path towards stabilization, or will this be dropped and only supported via try { .. } in Edition 2018+?

@Centril

This comment has been minimized.

Show comment
Hide comment
@Centril

Centril May 3, 2018

Contributor

@Nemo157 The latter.

Contributor

Centril commented May 3, 2018

@Nemo157 The latter.

@alexreg

This comment has been minimized.

Show comment
Hide comment
@alexreg

alexreg Jun 6, 2018

Contributor

Is there a two-statement try ... catch ... in the current proposal? If so, I don’t get the semantics.

Anyway, if this proposal is only about desugaring then I’m cool with it. i.e. Does a catch block just change where the ? operator exits to?

Contributor

alexreg commented Jun 6, 2018

Is there a two-statement try ... catch ... in the current proposal? If so, I don’t get the semantics.

Anyway, if this proposal is only about desugaring then I’m cool with it. i.e. Does a catch block just change where the ? operator exits to?

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