Skip to content

Commit

Permalink
Rollup merge of rust-lang#42275 - scottmcm:try-trait, r=nikomatsakis
Browse files Browse the repository at this point in the history
Lower `?` to `Try` instead of `Carrier`

The easy parts of rust-lang/rfcs#1859, whose FCP completed without further comments.

Just the trait and the lowering -- neither the error message improvements nor the insta-stable impl for Option nor exhaustive docs.

Based on a [github search](https://github.com/search?l=rust&p=1&q=question_mark_carrier&type=Code&utf8=%E2%9C%93), this will break the following:

- https://github.com/pfpacket/rust-9p/blob/00206e34c680198a0ac7c2f066cc2954187d4fac/src/serialize.rs#L38
- https://github.com/peterdelevoryas/bufparse/blob/b1325898f4fc2c67658049196c12da82548af350/src/result.rs#L50

The other results appear to be files from libcore or its tests.  I could also leave Carrier around after stage0 and `impl<T:Carrier> Try for T` if that would be better.

r? @nikomatsakis

Edit: Oh, and it might accidentally improve perf, based on rust-lang#37939 (comment), since `Try::into_result` for `Result` is an obvious no-op, unlike `Carrier::translate`.
  • Loading branch information
frewsxcv authored Jun 1, 2017
2 parents 0e8b9a8 + 3119e63 commit 90cbfa3
Show file tree
Hide file tree
Showing 7 changed files with 138 additions and 33 deletions.
1 change: 1 addition & 0 deletions src/doc/unstable-book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
50 changes: 50 additions & 0 deletions src/doc/unstable-book/src/library-features/try-trait.md
Original file line number Diff line number Diff line change
@@ -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<T> ops::Try for Option<T> {
type Ok = T;
type Error = None;
fn into_result(self) -> Result<T, None> {
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.
65 changes: 48 additions & 17 deletions src/libcore/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2918,15 +2918,9 @@ pub trait BoxPlace<Data: ?Sized> : Place<Data> {
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.
Expand All @@ -2945,6 +2939,7 @@ pub trait Carrier {
fn translate<T>(self) -> T where T: Carrier<Success=Self::Success, Error=Self::Error>;
}

#[cfg(stage0)]
#[unstable(feature = "question_mark_carrier", issue = "31436")]
impl<U, V> Carrier for Result<U, V> {
type Success = U;
Expand All @@ -2970,21 +2965,57 @@ impl<U, V> Carrier for Result<U, V> {

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<Self::Ok, Self::Error> {
Ok(())
}

fn from_ok(_: ()) -> _DummyErrorType {
_DummyErrorType
}

fn from_error(_: ()) -> _DummyErrorType {
_DummyErrorType
}
}

fn translate<T>(self) -> T
where T: Carrier<Success=(), Error=()>
{
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<Self::Ok, Self::Error>;

/// 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;
}
19 changes: 19 additions & 0 deletions src/libcore/result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`).
///
Expand Down Expand Up @@ -1108,3 +1109,21 @@ impl<A, E, V: FromIterator<A>> FromIterator<Result<A, E>> for Result<V, E> {
}
}
}

#[unstable(feature = "try_trait", issue = "42327")]
impl<T,E> ops::Try for Result<T, E> {
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)
}
}
12 changes: 6 additions & 6 deletions src/librustc/hir/lowering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2265,23 +2265,23 @@ impl<'a> LoweringContext<'a> {
ExprKind::Try(ref sub_expr) => {
// to:
//
// match Carrier::translate(<expr>) {
// match Try::into_result(<expr>) {
// 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(<expr>)
// Try::into_result(<expr>)
let discr = {
// expand <expr>
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]))
};
Expand Down Expand Up @@ -2327,7 +2327,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]))
Expand Down
18 changes: 8 additions & 10 deletions src/test/run-pass/try-operator-custom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,33 +8,31 @@
// 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<T, U> {
Awesome(T),
Terrible(U)
}

impl<U, V> Carrier for MyResult<U, V> {
type Success = U;
impl<U, V> Try for MyResult<U, V> {
type Ok = U;
type Error = V;

fn from_success(u: U) -> MyResult<U, V> {
fn from_ok(u: U) -> MyResult<U, V> {
MyResult::Awesome(u)
}

fn from_error(e: V) -> MyResult<U, V> {
MyResult::Terrible(e)
}

fn translate<T>(self) -> T
where T: Carrier<Success=U, Error=V>
{
fn into_result(self) -> Result<U, V> {
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),
}
}
}
Expand Down

0 comments on commit 90cbfa3

Please sign in to comment.