Skip to content
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

Lower ? to Try instead of Carrier #42275

Merged
merged 3 commits into from
Jun 1, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -2244,23 +2244,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 @@ -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]))
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