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..0c07329025bca --- /dev/null +++ b/src/doc/unstable-book/src/library-features/try-trait.md @@ -0,0 +1,50 @@ +# `try_trait` + +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. diff --git a/src/libcore/ops.rs b/src/libcore/ops.rs index c76cff4dc34d1..a1de8fe76e258 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 = "42327")] +pub trait Try { + /// The type of this value when viewed as successful. + #[unstable(feature = "try_trait", issue = "42327")] + type Ok; + /// The type of this value when viewed as failed. + #[unstable(feature = "try_trait", issue = "42327")] + 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 = "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 = "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 = "42327")] + fn from_ok(v: Self::Ok) -> Self; } diff --git a/src/libcore/result.rs b/src/libcore/result.rs index c46b0c1324de6..df7fff0df9270 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 = "42327")] +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), } } }