The `?` postfix operator is sugar equivalent to the try! macro, but is more amenable to chaining:
`File::open("foo")?.metadata()?.is_dir()`.
`?` is accepted on any *expression* that can return a `Result`, e.g. `x()?`, `y!()?`, `{z}?`,
`(w)?`, etc. And binds more tightly than unary operators, e.g. `!x?` is parsed as `!(x?)`.
cc #31436
hir::MatchSource::Normal => {
span_err!(cx.tcx.sess, pat.span, E0001, "unreachable pattern")
},
+
+ hir::MatchSource::TryDesugar => {
+ cx.tcx.sess.span_bug(pat.span, "unreachable try pattern")
+ },
}
}
Useful => (),
},
WhileLetDesugar,
ForLoopDesugar,
+ TryDesugar,
}
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
});
}
+ // Desugar ExprKind::Try
+ // From: `<expr>?`
+ ExprKind::Try(ref sub_expr) => {
+ // to:
+ //
+ // {
+ // match <expr> {
+ // Ok(val) => val,
+ // Err(err) => {
+ // return Err(From::from(err))
+ // }
+ // }
+ // }
+
+ return cache_ids(lctx, e.id, |lctx| {
+ // expand <expr>
+ let sub_expr = lower_expr(lctx, sub_expr);
+
+ // Ok(val) => val
+ let ok_arm = {
+ let val_ident = lctx.str_to_ident("val");
+ let val_pat = pat_ident(lctx, e.span, val_ident);
+ let val_expr = expr_ident(lctx, e.span, val_ident, None);
+ let ok_pat = pat_ok(lctx, e.span, val_pat);
+
+ arm(hir_vec![ok_pat], val_expr)
+ };
+
+ // Err(err) => return Err(From::from(err))
+ let err_arm = {
+ let err_ident = lctx.str_to_ident("err");
+ let from_expr = {
+ let path = std_path(lctx, &["convert", "From", "from"]);
+ let path = path_global(e.span, path);
+ let from = expr_path(lctx, path, None);
+ let err_expr = expr_ident(lctx, e.span, err_ident, None);
+
+ expr_call(lctx, e.span, from, hir_vec![err_expr], None)
+ };
+ let err_expr = {
+ let path = std_path(lctx, &["result", "Result", "Err"]);
+ let path = path_global(e.span, path);
+ let err_ctor = expr_path(lctx, path, None);
+ expr_call(lctx, e.span, err_ctor, hir_vec![from_expr], None)
+ };
+ let err_pat = pat_err(lctx, e.span, pat_ident(lctx, e.span, err_ident));
+ let ret_expr = expr(lctx, e.span,
+ hir::Expr_::ExprRet(Some(err_expr)), None);
+
+ arm(hir_vec![err_pat], ret_expr)
+ };
+
+ expr_match(lctx, e.span, sub_expr, hir_vec![err_arm, ok_arm],
+ hir::MatchSource::TryDesugar, None)
+ })
+ }
+
ExprKind::Mac(_) => panic!("Shouldn't exist here"),
},
span: e.span,
})
}
+fn pat_ok(lctx: &LoweringContext, span: Span, pat: P<hir::Pat>) -> P<hir::Pat> {
+ let ok = std_path(lctx, &["result", "Result", "Ok"]);
+ let path = path_global(span, ok);
+ pat_enum(lctx, span, path, hir_vec![pat])
+}
+
+fn pat_err(lctx: &LoweringContext, span: Span, pat: P<hir::Pat>) -> P<hir::Pat> {
+ let err = std_path(lctx, &["result", "Result", "Err"]);
+ let path = path_global(span, err);
+ pat_enum(lctx, span, path, hir_vec![pat])
+}
+
fn pat_some(lctx: &LoweringContext, span: Span, pat: P<hir::Pat>) -> P<hir::Pat> {
let some = std_path(lctx, &["option", "Option", "Some"]);
let path = path_global(span, some);
/// No-op: used solely so we can pretty-print faithfully
Paren(P<Expr>),
+
+ /// `expr?`
+ Try(P<Expr>),
}
/// The explicit Self type in a "qualified path". The actual
// a...b and ...b
("inclusive_range_syntax", "1.7.0", Some(28237), Active),
+
+ // `expr?`
+ ("question_mark", "1.9.0", Some(31436), Active)
];
// (changing above list without updating src/doc/reference.md makes @cmr sad)
pub staged_api: bool,
pub stmt_expr_attributes: bool,
pub deprecated: bool,
+ pub question_mark: bool,
}
impl Features {
staged_api: false,
stmt_expr_attributes: false,
deprecated: false,
+ question_mark: false,
}
}
}
e.span,
"inclusive range syntax is experimental");
}
+ ast::ExprKind::Try(..) => {
+ self.gate_feature("question_mark", e.span, "the `?` operator is not stable");
+ }
_ => {}
}
visit::walk_expr(self, e);
staged_api: cx.has_feature("staged_api"),
stmt_expr_attributes: cx.has_feature("stmt_expr_attributes"),
deprecated: cx.has_feature("deprecated"),
+ question_mark: cx.has_feature("question_mark"),
}
}
fields.move_map(|x| folder.fold_field(x)),
maybe_expr.map(|x| folder.fold_expr(x)))
},
- ExprKind::Paren(ex) => ExprKind::Paren(folder.fold_expr(ex))
+ ExprKind::Paren(ex) => ExprKind::Paren(folder.fold_expr(ex)),
+ ExprKind::Try(ex) => ExprKind::Try(folder.fold_expr(ex)),
},
span: folder.new_span(span),
attrs: attrs.map_thin_attrs(|v| fold_attrs(v, folder)),
let mut e = e0;
let mut hi;
loop {
+ // expr?
+ while self.eat(&token::Question) {
+ let hi = self.span.hi;
+ e = self.mk_expr(lo, hi, ExprKind::Try(e), None);
+ }
+
// expr.f
if self.eat(&token::Dot) {
match self.token {
}
};
-
if self.expr_is_complete(&lhs) {
// Semi-statement forms are odd. See https://github.com/rust-lang/rust/issues/29071
return Ok(lhs);
try!(self.print_inner_attributes_inline(attrs));
try!(self.print_expr(&e));
try!(self.pclose());
+ },
+ ast::ExprKind::Try(ref e) => {
+ try!(self.print_expr(e));
+ try!(word(&mut self.s, "?"))
}
}
try!(self.ann.post(self, NodeExpr(expr)));
visitor.visit_expr(&output.expr)
}
}
+ ExprKind::Try(ref subexpression) => {
+ visitor.visit_expr(subexpression)
+ }
}
visitor.visit_expr_post(expression)
--- /dev/null
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+macro_rules! id {
+ ($e:expr) => { $e }
+}
+
+fn main() {
+ id!(x?); //~ error: the `?` operator is not stable (see issue #31436)
+ //~^ help: add #![feature(question_mark)] to the crate attributes to enable
+ y?; //~ error: the `?` operator is not stable (see issue #31436)
+ //~^ help: add #![feature(question_mark)] to the crate attributes to enable
+}
--- /dev/null
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// `expr?` expands to:
+//
+// match expr {
+// Ok(val) => val,
+// Err(err) => return From::from(err),
+// }
+//
+// This test verifies that the expansion is hygienic, i.e. it's not affected by other `val` and
+// `err` bindings that may be in scope.
+
+#![feature(question_mark)]
+
+use std::num::ParseIntError;
+
+fn main() {
+ assert_eq!(parse(), Ok(1));
+}
+
+fn parse() -> Result<i32, ParseIntError> {
+ const val: char = 'a';
+ const err: char = 'b';
+
+ Ok("1".parse::<i32>()?)
+}
--- /dev/null
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(question_mark)]
+
+use std::fs::File;
+use std::io::{Read, self};
+use std::num::ParseIntError;
+use std::str::FromStr;
+
+fn on_method() -> Result<i32, ParseIntError> {
+ Ok("1".parse::<i32>()? + "2".parse::<i32>()?)
+}
+
+fn in_chain() -> Result<String, ParseIntError> {
+ Ok("3".parse::<i32>()?.to_string())
+}
+
+fn on_call() -> Result<i32, ParseIntError> {
+ fn parse<T: FromStr>(s: &str) -> Result<T, T::Err> {
+ s.parse()
+ }
+
+ Ok(parse("4")?)
+}
+
+fn nested() -> Result<i32, ParseIntError> {
+ Ok("5".parse::<i32>()?.to_string().parse()?)
+}
+
+fn on_path() -> Result<i32, ParseIntError> {
+ let x = "6".parse::<i32>();
+
+ Ok(x?)
+}
+
+fn on_macro() -> Result<i32, ParseIntError> {
+ macro_rules! id {
+ ($e:expr) => { $e }
+ }
+
+ Ok(id!("7".parse::<i32>())?)
+}
+
+fn on_parens() -> Result<i32, ParseIntError> {
+ let x = "8".parse::<i32>();
+
+ Ok((x)?)
+}
+
+fn on_block() -> Result<i32, ParseIntError> {
+ let x = "9".parse::<i32>();
+
+ Ok({x}?)
+}
+
+fn on_field() -> Result<i32, ParseIntError> {
+ struct Pair<A, B> { a: A, b: B }
+
+ let x = Pair { a: "10".parse::<i32>(), b: 0 };
+
+ Ok(x.a?)
+}
+
+fn on_tuple_field() -> Result<i32, ParseIntError> {
+ let x = ("11".parse::<i32>(), 0);
+
+ Ok(x.0?)
+}
+
+fn on_try() -> Result<i32, ParseIntError> {
+ let x = "12".parse::<i32>().map(|i| i.to_string().parse::<i32>());
+
+ Ok(x??)
+}
+
+fn on_binary_op() -> Result<i32, ParseIntError> {
+ let x = 13 - "14".parse::<i32>()?;
+ let y = "15".parse::<i32>()? - 16;
+ let z = "17".parse::<i32>()? - "18".parse::<i32>()?;
+
+ Ok(x + y + z)
+}
+
+fn on_index() -> Result<i32, ParseIntError> {
+ let x = [19];
+ let y = "0".parse::<usize>();
+
+ Ok(x[y?])
+}
+
+fn on_args() -> Result<i32, ParseIntError> {
+ fn sub(x: i32, y: i32) -> i32 { x - y }
+
+ let x = "20".parse();
+ let y = "21".parse();
+
+ Ok(sub(x?, y?))
+}
+
+fn on_if() -> Result<i32, ParseIntError> {
+ Ok(if true {
+ "22".parse::<i32>()
+ } else {
+ "23".parse::<i32>()
+ }?)
+}
+
+fn on_if_let() -> Result<i32, ParseIntError> {
+ Ok(if let Ok(..) = "24".parse::<i32>() {
+ "25".parse::<i32>()
+ } else {
+ "26".parse::<i32>()
+ }?)
+}
+
+fn on_match() -> Result<i32, ParseIntError> {
+ Ok(match "27".parse::<i32>() {
+ Err(..) => "28".parse::<i32>(),
+ Ok(..) => "29".parse::<i32>(),
+ }?)
+}
+
+fn tight_binding() -> Result<bool, ()> {
+ fn ok<T>(x: T) -> Result<T, ()> { Ok(x) }
+
+ let x = ok(true);
+ Ok(!x?)
+}
+
+// just type check
+fn merge_error() -> Result<i32, Error> {
+ let mut s = String::new();
+
+ File::open("foo.txt")?.read_to_string(&mut s)?;
+
+ Ok(s.parse::<i32>()? + 1)
+}
+
+fn main() {
+ assert_eq!(Ok(3), on_method());
+
+ assert_eq!(Ok("3".to_string()), in_chain());
+
+ assert_eq!(Ok(4), on_call());
+
+ assert_eq!(Ok(5), nested());
+
+ assert_eq!(Ok(6), on_path());
+
+ assert_eq!(Ok(7), on_macro());
+
+ assert_eq!(Ok(8), on_parens());
+
+ assert_eq!(Ok(9), on_block());
+
+ assert_eq!(Ok(10), on_field());
+
+ assert_eq!(Ok(11), on_tuple_field());
+
+ assert_eq!(Ok(12), on_try());
+
+ assert_eq!(Ok(-3), on_binary_op());
+
+ assert_eq!(Ok(19), on_index());
+
+ assert_eq!(Ok(-1), on_args());
+
+ assert_eq!(Ok(22), on_if());
+
+ assert_eq!(Ok(25), on_if_let());
+
+ assert_eq!(Ok(29), on_match());
+
+ assert_eq!(Ok(false), tight_binding());
+}
+
+enum Error {
+ Io(io::Error),
+ Parse(ParseIntError),
+}
+
+impl From<io::Error> for Error {
+ fn from(e: io::Error) -> Error {
+ Error::Io(e)
+ }
+}
+
+impl From<ParseIntError> for Error {
+ fn from(e: ParseIntError) -> Error {
+ Error::Parse(e)
+ }
+}