Skip to content

Commit

Permalink
Implement Try for ()
Browse files Browse the repository at this point in the history
  • Loading branch information
Jarcho committed Sep 25, 2023
1 parent 551c718 commit 66623f6
Show file tree
Hide file tree
Showing 10 changed files with 124 additions and 129 deletions.
87 changes: 87 additions & 0 deletions library/core/src/unit.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use crate::convert::Infallible;
use crate::iter::FromIterator;
use crate::ops::{ControlFlow, FromResidual, Try};

/// Collapses all unit items from an iterator into one.
///
Expand All @@ -19,3 +21,88 @@ impl FromIterator<()> for () {
iter.into_iter().for_each(|()| {})
}
}

/// Allows the unit type to be used with the question mark operator.
///
/// This allows more easily writing internal iteration functions which can take
/// both fallible and infallible closures, or visitors which can have both
/// fallible and infallible implementations.
///
/// # Examples
///
/// ```
/// #![feature(try_trait_v2)]
///
/// fn foreach<I, R>(iter: I, mut f: impl FnMut(I::Item) -> R) -> R
/// where
/// I: IntoIterator,
/// R: std::ops::Try<Output = ()>,
/// {
/// for x in iter {
/// f(x)?;
/// }
/// R::from_output(())
/// }
///
/// // prints everything
/// foreach([1, 2, 3], |x| println!("{x}"));
///
/// // prints everything until an error occurs
/// let _: Result<_, ()> = foreach([Ok(1), Ok(2), Err(()), Ok(3)], |x| {
/// println!("{}", x?);
/// Ok(())
/// });
/// ```
///
/// ```
/// #![feature(try_trait_v2)]
///
/// fn walk_children<V: Visitor>(visitor: &mut V, item: ()) -> V::ResultTy {
/// // can use the `?` operator if needed here.
/// <<V as Visitor>::ResultTy as std::ops::Try>::from_output(())
/// }
///
/// trait Visitor: Sized {
/// type ResultTy: std::ops::Try<Output = ()>;
///
/// fn visit_x(&mut self, item: ()) -> Self::ResultTy {
/// // can use the `?` operator if needed here.
/// walk_children(self, item)
/// }
/// // some `visit_*` functions
/// }
///
/// struct InfallibleVisitor;
///
/// impl Visitor for InfallibleVisitor {
/// type ResultTy = ();
/// // implement `visit_*` functions
/// }
///
/// struct FallibleVisitor;
///
/// impl Visitor for FallibleVisitor {
/// type ResultTy = std::ops::ControlFlow<()>;
/// // implement `visit_*` functions
/// }
/// ```
#[unstable(feature = "try_trait_v2", issue = "84277")]
impl Try for () {
type Output = ();
type Residual = Infallible;

fn from_output(_: ()) -> Self {
()
}

fn branch(self) -> ControlFlow<Infallible, ()> {
ControlFlow::Continue(())
}
}

#[unstable(feature = "try_trait_v2", issue = "84277")]
impl FromResidual<Infallible> for () {
fn from_residual(_: Infallible) -> Self {
()
}
}
1 change: 0 additions & 1 deletion tests/ui/async-await/issue-84841.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ async fn foo() {
// Adding an .await here avoids the ICE
test()?;
//~^ ERROR the `?` operator can only be applied to values that implement `Try`
//~| ERROR the `?` operator can only be used in an async function that returns
}

// Removing the const generic parameter here avoids the ICE
Expand Down
19 changes: 4 additions & 15 deletions tests/ui/async-await/issue-84841.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,11 @@ LL | test()?;
| ^^^^^^^ the `?` operator cannot be applied to type `impl Future<Output = ()>`
|
= help: the trait `Try` is not implemented for `impl Future<Output = ()>`

error[E0277]: the `?` operator can only be used in an async function that returns `Result` or `Option` (or another type that implements `FromResidual`)
--> $DIR/issue-84841.rs:9:11
|
LL | async fn foo() {
| ________________-
LL | | // Adding an .await here avoids the ICE
LL | | test()?;
| | ^ cannot use the `?` operator in an async function that returns `()`
LL | |
LL | |
LL | | }
| |_- this function should return `Result` or `Option` to accept `?`
help: consider `await`ing on the `Future`
|
= help: the trait `FromResidual<_>` is not implemented for `()`
LL | test().await?;
| ++++++

error: aborting due to 2 previous errors
error: aborting due to previous error

For more information about this error, try `rustc --explain E0277`.
28 changes: 4 additions & 24 deletions tests/ui/parser/ternary_operator.rs
Original file line number Diff line number Diff line change
@@ -1,69 +1,49 @@
// A good chunk of these errors aren't shown to the user, but are still
// required in the test for it to pass.

fn a() { //~ NOTE this function should return `Result` or `Option` to accept `?`
fn a() {
let x = 5 > 2 ? true : false;
//~^ ERROR Rust has no ternary operator
//~| HELP use an `if-else` expression instead
//~| ERROR the `?` operator can only be applied to values that implement `Try` [E0277]
//~| HELP the trait `Try` is not implemented for `{integer}`
//~| ERROR the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`) [E0277]
//~| HELP the trait `FromResidual<_>` is not implemented for `()`
//~| NOTE in this expansion of desugaring of operator `?`
//~| NOTE the `?` operator cannot be applied to type `{integer}`
//~| NOTE in this expansion of desugaring of operator `?`
//~| NOTE in this expansion of desugaring of operator `?`
//~| NOTE cannot use the `?` operator in a function that returns `()`
//~| NOTE in this expansion of desugaring of operator `?`
}

fn b() { //~ NOTE this function should return `Result` or `Option` to accept `?`
fn b() {
let x = 5 > 2 ? { true } : { false };
//~^ ERROR Rust has no ternary operator
//~| HELP use an `if-else` expression instead
//~| ERROR the `?` operator can only be applied to values that implement `Try` [E0277]
//~| HELP the trait `Try` is not implemented for `{integer}`
//~| ERROR the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`) [E0277]
//~| HELP the trait `FromResidual<_>` is not implemented for `()`
//~| NOTE in this expansion of desugaring of operator `?`
//~| NOTE the `?` operator cannot be applied to type `{integer}`
//~| NOTE in this expansion of desugaring of operator `?`
//~| NOTE in this expansion of desugaring of operator `?`
//~| NOTE cannot use the `?` operator in a function that returns `()`
//~| NOTE in this expansion of desugaring of operator `?`
}

fn c() { //~ NOTE this function should return `Result` or `Option` to accept `?`
fn c() {
let x = 5 > 2 ? f32::MAX : f32::MIN;
//~^ ERROR Rust has no ternary operator
//~| HELP use an `if-else` expression instead
//~| ERROR the `?` operator can only be applied to values that implement `Try` [E0277]
//~| HELP the trait `Try` is not implemented for `{integer}`
//~| ERROR the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`) [E0277]
//~| HELP the trait `FromResidual<_>` is not implemented for `()`
//~| NOTE in this expansion of desugaring of operator `?`
//~| NOTE the `?` operator cannot be applied to type `{integer}`
//~| NOTE in this expansion of desugaring of operator `?`
//~| NOTE in this expansion of desugaring of operator `?`
//~| NOTE cannot use the `?` operator in a function that returns `()`
//~| NOTE in this expansion of desugaring of operator `?`
}

fn main() { //~ NOTE this function should return `Result` or `Option` to accept `?`
fn main() {
let x = 5 > 2 ? { let x = vec![]: Vec<u16>; x } : { false };
//~^ ERROR Rust has no ternary operator
//~| HELP use an `if-else` expression instead
//~| ERROR expected one of `.`, `;`, `?`, `else`, or an operator, found `:`
//~| NOTE expected one of `.`, `;`, `?`, `else`, or an operator
//~| ERROR the `?` operator can only be applied to values that implement `Try` [E0277]
//~| HELP the trait `Try` is not implemented for `{integer}`
//~| ERROR the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`) [E0277]
//~| HELP the trait `FromResidual<_>` is not implemented for `()`
//~| NOTE type ascription syntax has been removed, see issue #101728 <https://github.com/rust-lang/rust/issues/101728>
//~| NOTE in this expansion of desugaring of operator `?`
//~| NOTE the `?` operator cannot be applied to type `{integer}`
//~| NOTE in this expansion of desugaring of operator `?`
//~| NOTE in this expansion of desugaring of operator `?`
//~| NOTE cannot use the `?` operator in a function that returns `()`
//~| NOTE in this expansion of desugaring of operator `?`
}
56 changes: 8 additions & 48 deletions tests/ui/parser/ternary_operator.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,31 @@ LL | let x = 5 > 2 ? true : false;
= help: use an `if-else` expression instead

error: Rust has no ternary operator
--> $DIR/ternary_operator.rs:21:19
--> $DIR/ternary_operator.rs:16:19
|
LL | let x = 5 > 2 ? { true } : { false };
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= help: use an `if-else` expression instead

error: Rust has no ternary operator
--> $DIR/ternary_operator.rs:37:19
--> $DIR/ternary_operator.rs:27:19
|
LL | let x = 5 > 2 ? f32::MAX : f32::MIN;
| ^^^^^^^^^^^^^^^^^^^^^^
|
= help: use an `if-else` expression instead

error: expected one of `.`, `;`, `?`, `else`, or an operator, found `:`
--> $DIR/ternary_operator.rs:53:37
--> $DIR/ternary_operator.rs:38:37
|
LL | let x = 5 > 2 ? { let x = vec![]: Vec<u16>; x } : { false };
| ^ expected one of `.`, `;`, `?`, `else`, or an operator
|
= note: type ascription syntax has been removed, see issue #101728 <https://github.com/rust-lang/rust/issues/101728>

error: Rust has no ternary operator
--> $DIR/ternary_operator.rs:53:19
--> $DIR/ternary_operator.rs:38:19
|
LL | let x = 5 > 2 ? { let x = vec![]: Vec<u16>; x } : { false };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand All @@ -46,70 +46,30 @@ LL | let x = 5 > 2 ? true : false;
|
= help: the trait `Try` is not implemented for `{integer}`

error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`)
--> $DIR/ternary_operator.rs:5:19
|
LL | fn a() {
| ------ this function should return `Result` or `Option` to accept `?`
LL | let x = 5 > 2 ? true : false;
| ^ cannot use the `?` operator in a function that returns `()`
|
= help: the trait `FromResidual<_>` is not implemented for `()`

error[E0277]: the `?` operator can only be applied to values that implement `Try`
--> $DIR/ternary_operator.rs:21:17
--> $DIR/ternary_operator.rs:16:17
|
LL | let x = 5 > 2 ? { true } : { false };
| ^^^ the `?` operator cannot be applied to type `{integer}`
|
= help: the trait `Try` is not implemented for `{integer}`

error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`)
--> $DIR/ternary_operator.rs:21:19
|
LL | fn b() {
| ------ this function should return `Result` or `Option` to accept `?`
LL | let x = 5 > 2 ? { true } : { false };
| ^ cannot use the `?` operator in a function that returns `()`
|
= help: the trait `FromResidual<_>` is not implemented for `()`

error[E0277]: the `?` operator can only be applied to values that implement `Try`
--> $DIR/ternary_operator.rs:37:17
--> $DIR/ternary_operator.rs:27:17
|
LL | let x = 5 > 2 ? f32::MAX : f32::MIN;
| ^^^ the `?` operator cannot be applied to type `{integer}`
|
= help: the trait `Try` is not implemented for `{integer}`

error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`)
--> $DIR/ternary_operator.rs:37:19
|
LL | fn c() {
| ------ this function should return `Result` or `Option` to accept `?`
LL | let x = 5 > 2 ? f32::MAX : f32::MIN;
| ^ cannot use the `?` operator in a function that returns `()`
|
= help: the trait `FromResidual<_>` is not implemented for `()`

error[E0277]: the `?` operator can only be applied to values that implement `Try`
--> $DIR/ternary_operator.rs:53:17
--> $DIR/ternary_operator.rs:38:17
|
LL | let x = 5 > 2 ? { let x = vec![]: Vec<u16>; x } : { false };
| ^^^ the `?` operator cannot be applied to type `{integer}`
|
= help: the trait `Try` is not implemented for `{integer}`

error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`)
--> $DIR/ternary_operator.rs:53:19
|
LL | fn main() {
| --------- this function should return `Result` or `Option` to accept `?`
LL | let x = 5 > 2 ? { let x = vec![]: Vec<u16>; x } : { false };
| ^ cannot use the `?` operator in a function that returns `()`
|
= help: the trait `FromResidual<_>` is not implemented for `()`

error: aborting due to 13 previous errors
error: aborting due to 9 previous errors

For more information about this error, try `rustc --explain E0277`.
3 changes: 0 additions & 3 deletions tests/ui/try-block/try-block-bad-type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,5 @@ pub fn main() {

let res: Result<i32, i32> = try { }; //~ ERROR type mismatch

let res: () = try { };
//~^ ERROR a `try` block must return `Result` or `Option`

let res: i32 = try { 5 }; //~ ERROR a `try` block must return `Result` or `Option`
}
12 changes: 2 additions & 10 deletions tests/ui/try-block/try-block-bad-type.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,14 @@ LL | let res: Result<i32, i32> = try { };
| ^ expected `i32`, found `()`

error[E0277]: a `try` block must return `Result` or `Option` (or another type that implements `Try`)
--> $DIR/try-block-bad-type.rs:17:25
|
LL | let res: () = try { };
| ^ could not wrap the final value of the block as `()` doesn't implement `Try`
|
= help: the trait `Try` is not implemented for `()`

error[E0277]: a `try` block must return `Result` or `Option` (or another type that implements `Try`)
--> $DIR/try-block-bad-type.rs:20:26
--> $DIR/try-block-bad-type.rs:17:26
|
LL | let res: i32 = try { 5 };
| ^ could not wrap the final value of the block as `i32` doesn't implement `Try`
|
= help: the trait `Try` is not implemented for `i32`

error: aborting due to 5 previous errors
error: aborting due to 4 previous errors

Some errors have detailed explanations: E0271, E0277.
For more information about an error, try `rustc --explain E0271`.
2 changes: 2 additions & 0 deletions tests/ui/try-trait/try-on-option-diagnostics.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ LL | x?;
| ^ cannot use the `?` operator in a method that returns `()`
|
= help: the trait `FromResidual<Option<Infallible>>` is not implemented for `()`
= help: the trait `FromResidual<Infallible>` is implemented for `()`

error[E0277]: the `?` operator can only be used in a trait method that returns `Result` or `Option` (or another type that implements `FromResidual`)
--> $DIR/try-on-option-diagnostics.rs:39:14
Expand All @@ -41,6 +42,7 @@ LL | x?;
| ^ cannot use the `?` operator in a trait method that returns `()`
|
= help: the trait `FromResidual<Option<Infallible>>` is not implemented for `()`
= help: the trait `FromResidual<Infallible>` is implemented for `()`

error: aborting due to 4 previous errors

Expand Down
7 changes: 3 additions & 4 deletions tests/ui/try-trait/try-operator-on-main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,15 @@ fn main() {
std::fs::File::open("foo")?; //~ ERROR the `?` operator can only

// a non-`Try` type on a non-`Try` fn
()?; //~ ERROR the `?` operator can only be applied to
//~^ ERROR the `?` operator can only be used in a function that
0i32?; //~ ERROR the `?` operator can only be applied to

// an unrelated use of `Try`
try_trait_generic::<()>(); //~ ERROR the trait bound
try_trait_generic::<i32>(); //~ ERROR the trait bound
}

fn try_trait_generic<T: Try>() -> T {
// and a non-`Try` object on a `Try` fn.
()?; //~ ERROR the `?` operator can only be applied to values that implement `Try`
0i32?; //~ ERROR the `?` operator can only be applied to values that implement `Try`

loop {}
}
Loading

0 comments on commit 66623f6

Please sign in to comment.