From ecde1e1d3b077f22f75ad34e12e35eef0b6c85f3 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sun, 7 May 2017 00:14:04 -0700 Subject: [PATCH 1/3] Lower `?` to `Try` instead of `Carrier` The easy parts of RFC 1859. (Just the trait and the lowering, none of the error message improvements nor the insta-stable impl for Option.) --- src/doc/unstable-book/src/SUMMARY.md | 1 + .../library-features/question-mark-carrier.md | 6 ++ .../src/library-features/try-trait.md | 7 ++ src/libcore/ops.rs | 65 ++++++++++++++----- src/libcore/result.rs | 19 ++++++ src/librustc/hir/lowering.rs | 12 ++-- src/test/run-pass/try-operator-custom.rs | 18 +++-- 7 files changed, 95 insertions(+), 33 deletions(-) create mode 100644 src/doc/unstable-book/src/library-features/try-trait.md diff --git a/src/doc/unstable-book/src/SUMMARY.md b/src/doc/unstable-book/src/SUMMARY.md index 456a683dc33c8..7684f89960cd5 100644 --- a/src/doc/unstable-book/src/SUMMARY.md +++ b/src/doc/unstable-book/src/SUMMARY.md @@ -208,6 +208,7 @@ - [toowned_clone_into](library-features/toowned-clone-into.md) - [trusted_len](library-features/trusted-len.md) - [try_from](library-features/try-from.md) + - [try_trait](library-features/try-trait.md) - [unicode](library-features/unicode.md) - [unique](library-features/unique.md) - [unsize](library-features/unsize.md) diff --git a/src/doc/unstable-book/src/library-features/question-mark-carrier.md b/src/doc/unstable-book/src/library-features/question-mark-carrier.md index 56154acc02bbf..a5e6965faec42 100644 --- a/src/doc/unstable-book/src/library-features/question-mark-carrier.md +++ b/src/doc/unstable-book/src/library-features/question-mark-carrier.md @@ -5,3 +5,9 @@ The tracking issue for this feature is: [#31436] [#31436]: https://github.com/rust-lang/rust/issues/31436 ------------------------ + +This feature has been superseded by [`try_trait`][try_trait]. + +It exists only in stage0 for bootstrapping. + +[try_trait]: library-features/try-trait.html diff --git a/src/doc/unstable-book/src/library-features/try-trait.md b/src/doc/unstable-book/src/library-features/try-trait.md new file mode 100644 index 0000000000000..f08929d4e3c2a --- /dev/null +++ b/src/doc/unstable-book/src/library-features/try-trait.md @@ -0,0 +1,7 @@ +# `try_trait` + +The tracking issue for this feature is: [#31436] + +[#31436]: https://github.com/rust-lang/rust/issues/31436 + +------------------------ diff --git a/src/libcore/ops.rs b/src/libcore/ops.rs index c76cff4dc34d1..912f3d4ff07d0 100644 --- a/src/libcore/ops.rs +++ b/src/libcore/ops.rs @@ -2918,15 +2918,9 @@ pub trait BoxPlace : Place { fn make_place() -> Self; } -/// A trait for types which have success and error states and are meant to work -/// with the question mark operator. -/// When the `?` operator is used with a value, whether the value is in the -/// success or error state is determined by calling `translate`. -/// -/// This trait is **very** experimental, it will probably be iterated on heavily -/// before it is stabilised. Implementors should expect change. Users of `?` -/// should not rely on any implementations of `Carrier` other than `Result`, -/// i.e., you should not expect `?` to continue to work with `Option`, etc. +/// This trait has been superseded by the `Try` trait, but must remain +/// here as `?` is still lowered to it in stage0 . +#[cfg(stage0)] #[unstable(feature = "question_mark_carrier", issue = "31436")] pub trait Carrier { /// The type of the value when computation succeeds. @@ -2945,6 +2939,7 @@ pub trait Carrier { fn translate(self) -> T where T: Carrier; } +#[cfg(stage0)] #[unstable(feature = "question_mark_carrier", issue = "31436")] impl Carrier for Result { type Success = U; @@ -2970,21 +2965,57 @@ impl Carrier for Result { struct _DummyErrorType; -impl Carrier for _DummyErrorType { - type Success = (); +impl Try for _DummyErrorType { + type Ok = (); type Error = (); - fn from_success(_: ()) -> _DummyErrorType { + fn into_result(self) -> Result { + Ok(()) + } + + fn from_ok(_: ()) -> _DummyErrorType { _DummyErrorType } fn from_error(_: ()) -> _DummyErrorType { _DummyErrorType } +} - fn translate(self) -> T - where T: Carrier - { - T::from_success(()) - } +/// A trait for customizing the behaviour of the `?` operator. +/// +/// A type implementing `Try` is one that has a canonical way to view it +/// in terms of a success/failure dichotomy. This trait allows both +/// extracting those success or failure values from an existing instance and +/// creating a new instance from a success or failure value. +#[unstable(feature = "try_trait", issue = "31436")] +pub trait Try { + /// The type of this value when viewed as successful. + #[unstable(feature = "try_trait", issue = "31436")] + type Ok; + /// The type of this value when viewed as failed. + #[unstable(feature = "try_trait", issue = "31436")] + type Error; + + /// Applies the "?" operator. A return of `Ok(t)` means that the + /// execution should continue normally, and the result of `?` is the + /// value `t`. A return of `Err(e)` means that execution should branch + /// to the innermost enclosing `catch`, or return from the function. + /// + /// If an `Err(e)` result is returned, the value `e` will be "wrapped" + /// in the return type of the enclosing scope (which must itself implement + /// `Try`). Specifically, the value `X::from_error(From::from(e))` + /// is returned, where `X` is the return type of the enclosing function. + #[unstable(feature = "try_trait", issue = "31436")] + fn into_result(self) -> Result; + + /// Wrap an error value to construct the composite result. For example, + /// `Result::Err(x)` and `Result::from_error(x)` are equivalent. + #[unstable(feature = "try_trait", issue = "31436")] + fn from_error(v: Self::Error) -> Self; + + /// Wrap an OK value to construct the composite result. For example, + /// `Result::Ok(x)` and `Result::from_ok(x)` are equivalent. + #[unstable(feature = "try_trait", issue = "31436")] + fn from_ok(v: Self::Ok) -> Self; } diff --git a/src/libcore/result.rs b/src/libcore/result.rs index c46b0c1324de6..a02c19f5f38b5 100644 --- a/src/libcore/result.rs +++ b/src/libcore/result.rs @@ -242,6 +242,7 @@ use fmt; use iter::{FromIterator, FusedIterator, TrustedLen}; +use ops; /// `Result` is a type that represents either success (`Ok`) or failure (`Err`). /// @@ -1108,3 +1109,21 @@ impl> FromIterator> for Result { } } } + +#[unstable(feature = "try_trait", issue = "31436")] +impl ops::Try for Result { + type Ok = T; + type Error = E; + + fn into_result(self) -> Self { + self + } + + fn from_ok(v: T) -> Self { + Ok(v) + } + + fn from_error(v: E) -> Self { + Err(v) + } +} \ No newline at end of file diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index d359c69d3a092..68cee5b93fea8 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -2244,23 +2244,23 @@ impl<'a> LoweringContext<'a> { ExprKind::Try(ref sub_expr) => { // to: // - // match Carrier::translate() { + // match Try::into_result() { // Ok(val) => #[allow(unreachable_code)] val, // Err(err) => #[allow(unreachable_code)] // // If there is an enclosing `catch {...}` - // break 'catch_target Carrier::from_error(From::from(err)), + // break 'catch_target Try::from_error(From::from(err)), // // Otherwise - // return Carrier::from_error(From::from(err)), + // return Try::from_error(From::from(err)), // } let unstable_span = self.allow_internal_unstable("?", e.span); - // Carrier::translate() + // Try::into_result() let discr = { // expand let sub_expr = self.lower_expr(sub_expr); - let path = &["ops", "Carrier", "translate"]; + let path = &["ops", "Try", "into_result"]; let path = P(self.expr_std_path(unstable_span, path, ThinVec::new())); P(self.expr_call(e.span, path, hir_vec![sub_expr])) }; @@ -2306,7 +2306,7 @@ impl<'a> LoweringContext<'a> { self.expr_call(e.span, from, hir_vec![err_expr]) }; let from_err_expr = { - let path = &["ops", "Carrier", "from_error"]; + let path = &["ops", "Try", "from_error"]; let from_err = P(self.expr_std_path(unstable_span, path, ThinVec::new())); P(self.expr_call(e.span, from_err, hir_vec![from_expr])) diff --git a/src/test/run-pass/try-operator-custom.rs b/src/test/run-pass/try-operator-custom.rs index 577d19a58960d..82ba70c945944 100644 --- a/src/test/run-pass/try-operator-custom.rs +++ b/src/test/run-pass/try-operator-custom.rs @@ -8,20 +8,20 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(question_mark, question_mark_carrier)] +#![feature(try_trait)] -use std::ops::Carrier; +use std::ops::Try; enum MyResult { Awesome(T), Terrible(U) } -impl Carrier for MyResult { - type Success = U; +impl Try for MyResult { + type Ok = U; type Error = V; - fn from_success(u: U) -> MyResult { + fn from_ok(u: U) -> MyResult { MyResult::Awesome(u) } @@ -29,12 +29,10 @@ impl Carrier for MyResult { MyResult::Terrible(e) } - fn translate(self) -> T - where T: Carrier - { + fn into_result(self) -> Result { match self { - MyResult::Awesome(u) => T::from_success(u), - MyResult::Terrible(e) => T::from_error(e), + MyResult::Awesome(u) => Ok(u), + MyResult::Terrible(e) => Err(e), } } } From 7a87469af7be24f6653ab59be0f1a544f4f3eb80 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Wed, 31 May 2017 01:30:13 -0700 Subject: [PATCH 2/3] Give the `try_trait` feature its own tracking issue --- .../unstable-book/src/library-features/try-trait.md | 4 ++-- src/libcore/ops.rs | 12 ++++++------ src/libcore/result.rs | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/doc/unstable-book/src/library-features/try-trait.md b/src/doc/unstable-book/src/library-features/try-trait.md index f08929d4e3c2a..7289e95c2a002 100644 --- a/src/doc/unstable-book/src/library-features/try-trait.md +++ b/src/doc/unstable-book/src/library-features/try-trait.md @@ -1,7 +1,7 @@ # `try_trait` -The tracking issue for this feature is: [#31436] +The tracking issue for this feature is: [#42327] -[#31436]: https://github.com/rust-lang/rust/issues/31436 +[#42327]: https://github.com/rust-lang/rust/issues/42327 ------------------------ diff --git a/src/libcore/ops.rs b/src/libcore/ops.rs index 912f3d4ff07d0..a1de8fe76e258 100644 --- a/src/libcore/ops.rs +++ b/src/libcore/ops.rs @@ -2988,13 +2988,13 @@ impl Try for _DummyErrorType { /// in terms of a success/failure dichotomy. This trait allows both /// extracting those success or failure values from an existing instance and /// creating a new instance from a success or failure value. -#[unstable(feature = "try_trait", issue = "31436")] +#[unstable(feature = "try_trait", issue = "42327")] pub trait Try { /// The type of this value when viewed as successful. - #[unstable(feature = "try_trait", issue = "31436")] + #[unstable(feature = "try_trait", issue = "42327")] type Ok; /// The type of this value when viewed as failed. - #[unstable(feature = "try_trait", issue = "31436")] + #[unstable(feature = "try_trait", issue = "42327")] type Error; /// Applies the "?" operator. A return of `Ok(t)` means that the @@ -3006,16 +3006,16 @@ pub trait Try { /// in the return type of the enclosing scope (which must itself implement /// `Try`). Specifically, the value `X::from_error(From::from(e))` /// is returned, where `X` is the return type of the enclosing function. - #[unstable(feature = "try_trait", issue = "31436")] + #[unstable(feature = "try_trait", issue = "42327")] fn into_result(self) -> Result; /// Wrap an error value to construct the composite result. For example, /// `Result::Err(x)` and `Result::from_error(x)` are equivalent. - #[unstable(feature = "try_trait", issue = "31436")] + #[unstable(feature = "try_trait", issue = "42327")] fn from_error(v: Self::Error) -> Self; /// Wrap an OK value to construct the composite result. For example, /// `Result::Ok(x)` and `Result::from_ok(x)` are equivalent. - #[unstable(feature = "try_trait", issue = "31436")] + #[unstable(feature = "try_trait", issue = "42327")] fn from_ok(v: Self::Ok) -> Self; } diff --git a/src/libcore/result.rs b/src/libcore/result.rs index a02c19f5f38b5..df7fff0df9270 100644 --- a/src/libcore/result.rs +++ b/src/libcore/result.rs @@ -1110,7 +1110,7 @@ impl> FromIterator> for Result { } } -#[unstable(feature = "try_trait", issue = "31436")] +#[unstable(feature = "try_trait", issue = "42327")] impl ops::Try for Result { type Ok = T; type Error = E; From 3119e634e178d8acaed4a2d4a9d52e3b76ae79cf Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Wed, 31 May 2017 02:16:01 -0700 Subject: [PATCH 3/3] Add some try_trait ramblings to the unstable book --- .../src/library-features/try-trait.md | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/doc/unstable-book/src/library-features/try-trait.md b/src/doc/unstable-book/src/library-features/try-trait.md index 7289e95c2a002..0c07329025bca 100644 --- a/src/doc/unstable-book/src/library-features/try-trait.md +++ b/src/doc/unstable-book/src/library-features/try-trait.md @@ -5,3 +5,46 @@ The tracking issue for this feature is: [#42327] [#42327]: https://github.com/rust-lang/rust/issues/42327 ------------------------ + +This introduces a new trait `Try` for extending the `?` operator to types +other than `Result` (a part of [RFC 1859]). The trait provides the canonical +way to _view_ a type in terms of a success/failure dichotomy. This will +allow `?` to supplant the `try_opt!` macro on `Option` and the `try_ready!` +macro on `Poll`, among other things. + +[RFC 1859]: https://github.com/rust-lang/rfcs/pull/1859 + +Here's an example implementation of the trait: + +```rust,ignore +/// A distinct type to represent the `None` value of an `Option`. +/// +/// This enables using the `?` operator on `Option`; it's rarely useful alone. +#[derive(Debug)] +#[unstable(feature = "try_trait", issue = "42327")] +pub struct None { _priv: () } + +#[unstable(feature = "try_trait", issue = "42327")] +impl ops::Try for Option { + type Ok = T; + type Error = None; + + fn into_result(self) -> Result { + self.ok_or(None { _priv: () }) + } + + fn from_ok(v: T) -> Self { + Some(v) + } + + fn from_error(_: None) -> Self { + None + } +} +``` + +Note the `Error` associated type here is a new marker. The `?` operator +allows interconversion between different `Try` implementers only when +the error type can be converted `Into` the error type of the enclosing +function (or catch block). Having a distinct error type (as opposed to +just `()`, or similar) restricts this to where it's semantically meaningful.