Skip to content

Use a Carrier trait with the ? operator #33389

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

Closed
wants to merge 1 commit into from
Closed
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
125 changes: 125 additions & 0 deletions src/libcore/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ use fmt;
use convert::From;
use marker::{Sized, Unsize};
use num::One;
use result::Result::{self, Ok, Err};
use option::Option::{self, Some, None};

/// The `Drop` trait is used to run some code when a value goes out of scope.
/// This is sometimes called a 'destructor'.
Expand Down Expand Up @@ -2155,3 +2157,126 @@ pub trait BoxPlace<Data: ?Sized> : Place<Data> {
/// Creates a globally fresh 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.
#[unstable(feature = "question_mark_carrier", issue = "31436")]
pub trait Carrier {
/// The type of the value when computation succeeds.
type Success;
/// The type of the value when computation errors out.
type Error;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps EarlyReturn could be a better name than Error, as it more closely resembles what actually happens? Returning early does not have to be an error.


/// Create a `Carrier` from a success value.
fn from_success(Self::Success) -> Self;

/// Create a `Carrier` from an error value.
fn from_error(Self::Error) -> Self;

/// Translate this `Carrier` to another implementation of `Carrier` with the
/// same associated types.
fn translate<T>(self) -> T where T: Carrier<Success=Self::Success, Error=Self::Error>;
}

#[unstable(feature = "question_mark_carrier", issue = "31436")]
impl<U, V> Carrier for Result<U, V> {
type Success = U;
type Error = V;

fn from_success(u: U) -> Result<U, V> {
Ok(u)
}

fn from_error(e: V) -> Result<U, V> {
Err(e)
}

fn translate<T>(self) -> T
where T: Carrier<Success=U, Error=V>
{
match self {
Ok(u) => T::from_success(u),
Err(e) => T::from_error(e),
}
}
}

#[unstable(feature = "question_mark_carrier", issue = "31436")]
impl<U> Carrier for Option<U> {
type Success = U;
type Error = ();

fn from_success(u: U) -> Option<U> {
Some(u)
}

fn from_error(_: ()) -> Option<U> {
None
}

fn translate<T>(self) -> T
where T: Carrier<Success=U, Error=()>
{
match self {
Some(u) => T::from_success(u),
None => T::from_error(()),
}
}
}

// Implementing Carrier for bools means it's easy to write short-circuiting
// functions. E.g.,
// ```
// fn foo() -> bool {
// if !(f() || g()) {
// return false;
// }
//
// some_computation();
// if h() {
// return false;
// }
//
// more_computation();
// i()
// }
// ```
// becomes
// ```
// fn foo() -> bool {
// (f() || g())?;
// some_computation();
// (!h())?;
// more_computation();
// i()
// }
// ```
#[unstable(feature = "question_mark_carrier", issue = "31436")]
impl Carrier for bool {
type Success = ();
type Error = ();

fn from_success(_: ()) -> bool {
true
}

fn from_error(_: ()) -> bool {
false
}

fn translate<T>(self) -> T
where T: Carrier<Success=(), Error=()>
{
match self {
true => T::from_success(()),
false => T::from_error(()),
}
}
}
89 changes: 43 additions & 46 deletions src/librustc/hir/lowering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -955,7 +955,7 @@ impl<'a> LoweringContext<'a> {
let inplace_finalize = ["ops", "InPlace", "finalize"];

let make_call = |this: &mut LoweringContext, p, args| {
let path = this.core_path(e.span, p);
let path = this.std_path(e.span, p);
let path = this.expr_path(path, None);
this.expr_call(e.span, path, args, None)
};
Expand Down Expand Up @@ -1147,15 +1147,13 @@ impl<'a> LoweringContext<'a> {
ast_expr: &Expr,
path: &[&str],
fields: &[(&str, &P<Expr>)]) -> P<hir::Expr> {
let strs = this.std_path(&iter::once(&"ops")
.chain(path)
.map(|s| *s)
.collect::<Vec<_>>());

let structpath = this.path_global(ast_expr.span, strs);
let struct_path = this.std_path(ast_expr.span,
&iter::once(&"ops").chain(path)
.map(|s| *s)
.collect::<Vec<_>>());

let hir_expr = if fields.len() == 0 {
this.expr_path(structpath, ast_expr.attrs.clone())
this.expr_path(struct_path, ast_expr.attrs.clone())
} else {
let fields = fields.into_iter().map(|&(s, e)| {
let expr = this.lower_expr(&e);
Expand All @@ -1168,7 +1166,7 @@ impl<'a> LoweringContext<'a> {
}).collect();
let attrs = ast_expr.attrs.clone();

this.expr_struct(ast_expr.span, structpath, fields, None, attrs)
this.expr_struct(ast_expr.span, struct_path, fields, None, attrs)
};

this.signal_block_expr(hir_vec![],
Expand Down Expand Up @@ -1452,11 +1450,7 @@ impl<'a> LoweringContext<'a> {

// `match ::std::iter::Iterator::next(&mut iter) { ... }`
let match_expr = {
let next_path = {
let strs = self.std_path(&["iter", "Iterator", "next"]);

self.path_global(e.span, strs)
};
let next_path = self.std_path(e.span, &["iter", "Iterator", "next"]);
let iter = self.expr_ident(e.span, iter, None, iter_pat.id);
let ref_mut_iter = self.expr_mut_addr_of(e.span, iter, None);
let next_path = self.expr_path(next_path, None);
Expand All @@ -1483,11 +1477,8 @@ impl<'a> LoweringContext<'a> {

// `match ::std::iter::IntoIterator::into_iter(<head>) { ... }`
let into_iter_expr = {
let into_iter_path = {
let strs = self.std_path(&["iter", "IntoIterator", "into_iter"]);

self.path_global(e.span, strs)
};
let into_iter_path = self.std_path(e.span,
&["iter", "IntoIterator", "into_iter"]);

let into_iter = self.expr_path(into_iter_path, None);
self.expr_call(e.span, into_iter, hir_vec![head], None)
Expand Down Expand Up @@ -1517,16 +1508,25 @@ impl<'a> LoweringContext<'a> {
// to:
//
// {
// match <expr> {
// match { Carrier::translate( { <expr> } ) } {
// Ok(val) => val,
// Err(err) => {
// return Err(From::from(err))
// }
// Err(err) => return { Carrier::from_error(From::from(err)) },
// }
// }

// expand <expr>
let sub_expr = self.lower_expr(sub_expr);
// { Carrier::translate( { <expr> } ) }
let discr = {
// expand <expr>
let sub_expr = self.lower_expr(sub_expr);
let sub_expr = self.signal_block_expr(hir_vec![], sub_expr, e.span,
hir::PopUnstableBlock, None);

let path = self.std_path(e.span, &["ops", "Carrier", "translate"]);
let path = self.expr_path(path, None);
let call = self.expr_call(e.span, path, hir_vec![sub_expr], None);

self.signal_block_expr(hir_vec![], call, e.span, hir::PushUnstableBlock, None)
};

// Ok(val) => val
let ok_arm = {
Expand All @@ -1538,32 +1538,33 @@ impl<'a> LoweringContext<'a> {
self.arm(hir_vec![ok_pat], val_expr)
};

// Err(err) => return Err(From::from(err))
// Err(err) => return { Carrier::from_error(From::from(err)) },
let err_arm = {
let err_ident = self.str_to_ident("err");
let err_local = self.pat_ident(e.span, err_ident);
let from_expr = {
let path = self.std_path(&["convert", "From", "from"]);
let path = self.path_global(e.span, path);
let path = self.std_path(e.span, &["convert", "From", "from"]);
let from = self.expr_path(path, None);
let err_expr = self.expr_ident(e.span, err_ident, None, err_local.id);

self.expr_call(e.span, from, hir_vec![err_expr], None)
};
let err_expr = {
let path = self.std_path(&["result", "Result", "Err"]);
let path = self.path_global(e.span, path);
let err_ctor = self.expr_path(path, None);
self.expr_call(e.span, err_ctor, hir_vec![from_expr], None)
let from_err_expr = {
let path = self.std_path(e.span, &["ops", "Carrier", "from_error"]);
let from_err = self.expr_path(path, None);
let call = self.expr_call(e.span, from_err, hir_vec![from_expr], None);

self.signal_block_expr(hir_vec![], call, e.span,
hir::PushUnstableBlock, None)
};
let err_pat = self.pat_err(e.span, err_local);
let ret_expr = self.expr(e.span,
hir::Expr_::ExprRet(Some(err_expr)), None);
hir::Expr_::ExprRet(Some(from_err_expr)), None);

self.arm(hir_vec![err_pat], ret_expr)
};

return self.expr_match(e.span, sub_expr, hir_vec![err_arm, ok_arm],
return self.expr_match(e.span, discr, hir_vec![err_arm, ok_arm],
hir::MatchSource::TryDesugar, None);
}

Expand Down Expand Up @@ -1798,26 +1799,22 @@ impl<'a> LoweringContext<'a> {
}

fn pat_ok(&mut self, span: Span, pat: P<hir::Pat>) -> P<hir::Pat> {
let ok = self.std_path(&["result", "Result", "Ok"]);
let path = self.path_global(span, ok);
let path = self.std_path(span, &["result", "Result", "Ok"]);
self.pat_enum(span, path, hir_vec![pat])
}

fn pat_err(&mut self, span: Span, pat: P<hir::Pat>) -> P<hir::Pat> {
let err = self.std_path(&["result", "Result", "Err"]);
let path = self.path_global(span, err);
let path = self.std_path(span, &["result", "Result", "Err"]);
self.pat_enum(span, path, hir_vec![pat])
}

fn pat_some(&mut self, span: Span, pat: P<hir::Pat>) -> P<hir::Pat> {
let some = self.std_path(&["option", "Option", "Some"]);
let path = self.path_global(span, some);
let path = self.std_path(span, &["option", "Option", "Some"]);
self.pat_enum(span, path, hir_vec![pat])
}

fn pat_none(&mut self, span: Span) -> P<hir::Pat> {
let none = self.std_path(&["option", "Option", "None"]);
let path = self.path_global(span, none);
let path = self.std_path(span, &["option", "Option", "None"]);
self.pat_enum(span, path, hir_vec![])
}

Expand Down Expand Up @@ -1915,7 +1912,7 @@ impl<'a> LoweringContext<'a> {
}
}

fn std_path(&mut self, components: &[&str]) -> Vec<hir::Ident> {
fn std_path_components(&mut self, components: &[&str]) -> Vec<hir::Ident> {
let mut v = Vec::new();
if let Some(s) = self.crate_root {
v.push(hir::Ident::from_name(token::intern(s)));
Expand All @@ -1926,8 +1923,8 @@ impl<'a> LoweringContext<'a> {

// Given suffix ["b","c","d"], returns path `::std::b::c::d` when
// `fld.cx.use_std`, and `::core::b::c::d` otherwise.
fn core_path(&mut self, span: Span, components: &[&str]) -> hir::Path {
let idents = self.std_path(components);
fn std_path(&mut self, span: Span, components: &[&str]) -> hir::Path {
let idents = self.std_path_components(components);
self.path_global(span, idents)
}

Expand Down
2 changes: 1 addition & 1 deletion src/libstd/net/addr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@ fn resolve_socket_addr(s: &str, p: u16) -> io::Result<vec::IntoIter<SocketAddr>>
a.set_port(p);
a
})
}).collect()?;
}).collect::<Result<_, _>>()?;
Ok(v.into_iter())
}

Expand Down
Loading