Auto merge of #31954 - japaric:rfc243, r=nikomatsakis
implement the `?` operator
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
---
cc @aturon @eddyb
This commit is contained in:
commit
3af60f831f
26 changed files with 369 additions and 16 deletions
20
src/test/compile-fail/feature-gate-try-operator.rs
Normal file
20
src/test/compile-fail/feature-gate-try-operator.rs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
// 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
|
||||
}
|
||||
|
|
@ -12,5 +12,5 @@
|
|||
|
||||
fn main() {
|
||||
let t = (42, 42);
|
||||
t.0::<isize>; //~ ERROR expected one of `.`, `;`, `}`, or an operator, found `::`
|
||||
t.0::<isize>; //~ ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `::`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,4 +15,4 @@
|
|||
fn main()
|
||||
{
|
||||
let x = 3
|
||||
} //~ ERROR: expected one of `.`, `;`, or an operator, found `}`
|
||||
} //~ ERROR: expected one of `.`, `;`, `?`, or an operator, found `}`
|
||||
|
|
|
|||
|
|
@ -12,6 +12,6 @@
|
|||
|
||||
fn main() {
|
||||
assert_eq!(1, 2)
|
||||
assert_eq!(3, 4) //~ ERROR expected one of `.`, `;`, `}`, or an operator, found `assert_eq`
|
||||
assert_eq!(3, 4) //~ ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `assert_eq`
|
||||
println!("hello");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ fn main() {
|
|||
let foo =
|
||||
match //~ NOTE did you mean to remove this `match` keyword?
|
||||
Some(4).unwrap_or_else(5)
|
||||
; //~ ERROR expected one of `.`, `{`, or an operator, found `;`
|
||||
; //~ ERROR expected one of `.`, `?`, `{`, or an operator, found `;`
|
||||
|
||||
println!("{}", foo)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,5 +14,5 @@
|
|||
|
||||
pub fn main() {
|
||||
let r = 1..2..3;
|
||||
//~^ ERROR expected one of `.`, `;`, or an operator, found `..`
|
||||
//~^ ERROR expected one of `.`, `;`, `?`, or an operator, found `..`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,5 +14,5 @@
|
|||
|
||||
pub fn main() {
|
||||
let r = ..1..2;
|
||||
//~^ ERROR expected one of `.`, `;`, or an operator, found `..`
|
||||
//~^ ERROR expected one of `.`, `;`, `?`, or an operator, found `..`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,5 +12,5 @@
|
|||
|
||||
static s: &'static str =
|
||||
r#"
|
||||
"## //~ ERROR expected one of `.`, `;`, or an operator, found `#`
|
||||
"## //~ ERROR expected one of `.`, `;`, `?`, or an operator, found `#`
|
||||
;
|
||||
|
|
|
|||
|
|
@ -13,5 +13,5 @@
|
|||
fn f() {
|
||||
let v = [mut 1, 2, 3, 4];
|
||||
//~^ ERROR expected identifier, found keyword `mut`
|
||||
//~^^ ERROR expected one of `!`, `,`, `.`, `::`, `;`, `]`, `{`, or an operator, found `1`
|
||||
//~^^ ERROR expected one of `!`, `,`, `.`, `::`, `;`, `?`, `]`, `{`, or an operator, found `1`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,5 +13,5 @@
|
|||
fn f() {
|
||||
let a_box = box mut 42;
|
||||
//~^ ERROR expected identifier, found keyword `mut`
|
||||
//~^^ ERROR expected one of `!`, `.`, `::`, `;`, `{`, or an operator, found `42`
|
||||
//~^^ ERROR expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `42`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,5 +18,5 @@ fn removed_with() {
|
|||
|
||||
let a = S { foo: (), bar: () };
|
||||
let b = S { foo: () with a };
|
||||
//~^ ERROR expected one of `,`, `.`, `}`, or an operator, found `with`
|
||||
//~^ ERROR expected one of `,`, `.`, `?`, `}`, or an operator, found `with`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ impl Foo {
|
|||
fn main() {
|
||||
for x in Foo {
|
||||
x: 3 //~ ERROR expected type, found `3`
|
||||
}.hi() { //~ ERROR expected one of `.`, `;`, `}`, or an operator, found `{`
|
||||
}.hi() { //~ ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `{`
|
||||
println!("yo");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ impl Foo {
|
|||
fn main() {
|
||||
if Foo {
|
||||
x: 3 //~ ERROR expected type, found `3`
|
||||
}.hi() { //~ ERROR expected one of `.`, `;`, `}`, or an operator, found `{`
|
||||
}.hi() { //~ ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `{`
|
||||
println!("yo");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,6 @@ fn main() {
|
|||
} {
|
||||
Foo {
|
||||
x: x
|
||||
} => {} //~ ERROR expected one of `.`, `;`, `}`, or an operator, found `=>`
|
||||
} => {} //~ ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `=>`
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ impl Foo {
|
|||
fn main() {
|
||||
while Foo {
|
||||
x: 3 //~ ERROR expected type, found `3`
|
||||
}.hi() { //~ ERROR expected one of `.`, `;`, `}`, or an operator, found `{`
|
||||
}.hi() { //~ ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `{`
|
||||
println!("yo");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
34
src/test/run-pass/try-operator-hygiene.rs
Normal file
34
src/test/run-pass/try-operator-hygiene.rs
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
// 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>()?)
|
||||
}
|
||||
200
src/test/run-pass/try-operator.rs
Normal file
200
src/test/run-pass/try-operator.rs
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
// 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)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue