12

Using this file:

use std::env;

fn main() {
    println!("{}", env::args().nth(3)?);
}

I get this error:

error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
 --> src/main.rs:4:20
  |
4 |     println!("{}", env::args().nth(3)?);
  |                    ^^^^^^^^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()`
  |
  = help: the trait `std::ops::Try` is not implemented for `()`
  = note: required by `std::ops::Try::from_error`

However this is confusing because nth does return Option:

fn nth(&mut self, n: usize) -> Option<Self::Item>

Am I misunderstanding the documentation or is this a bug?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • You're applying `?` to the result of `nth`, which is different from using it _inside_ it. You're calling `nth`, not implementing it. `main` is the function you're writing it in, which doesn't return `Option`. – Tim Vermeulen Dec 05 '18 at 13:31

3 Answers3

9

The return type of main must implement std::process::Termination(currently it's an unstable trait). If you look at the end of the documentation, you will see:

impl Termination for !
impl Termination for ()
impl Termination for ExitCode
impl<E: Debug> Termination for Result<!, E>
impl<E: Debug> Termination for Result<(), E>

If you want to return an Option you must implement the trait on it. This is not practical because you can't implement a trait on foreign type, so the best solution is to convert Option<T> to Result<T, E>:

use std::env;

fn main() -> Result<(), Box<std::error::Error>> {
    println!("{}", env::args().nth(3).ok_or("Missing argument")?);
    Ok(())
}

See also:

Stargateur
  • 24,473
  • 8
  • 65
  • 91
2

The ? operator will cause the function containing it to return None if the value the ? is applied to is None.

This means you can write

fn not_main() -> Option<()> {
    println!("{}", std::env::args().nth(3)?);
    Ok(())
}

since nth returns an Option<Item> and not_main returns an Option<()>.

However, your main does not return an Option, hence ? can't work inside it.

How you work around this will depend on what you want to do in the case of a missing argument. The most brutal solution is to unwrap instead - which will cause your code to panic.

fn main() {
    println!("{}", env::args().nth(3).unwrap())
}

An alternative is to match and handle the missing case

fn main() {
    match std::env::args().nth(3) {
        Some(ref v) => println!("{}", v),
        None => println!("Missing argument"),
    }
}

Since Option supports Debug you could print the debug version - which will output None, or Some("arg3").

fn main() {
    println!("{:?}", std::env::args().nth(3));
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Michael Anderson
  • 70,661
  • 7
  • 134
  • 187
  • I thought there was a way to return Result from `main` ? – Malice Dec 05 '18 at 05:47
  • A better reference for returning Result from main is https://rust-lang-nursery.github.io/edition-guide/rust-2018/error-handling-and-panics/question-mark-in-main-and-tests.html – Michael Anderson Dec 05 '18 at 23:41
0

If you really want to use ? on a Option value in main, you probably need to implement you own Option.

In your case, nothing::Probably is a better Option.

Example (you need nightly toolchain to run it):

use nothing::{Nothing, Probably, Something};

fn get_args() -> Probably<Vec<String>> {
    match std::env::args().skip(1).collect::<Vec<String>>() {
        args @ _ if args.len() > 0 => Something(args),
        _ => Nothing,
    }
}

fn main() -> Probably<Vec<String>> {
    let some_args = get_args();

    println!("some_args = {some_args:?}");

    let args = some_args?; // <- it returns here if some_args is Nothing

    println!("args = {args:?}");

    Something(args)
}

It works because Probably implements std::process::Termination so you can return it from you main function. Additionally it implements std::ops::Try so you can use ? on it.

btwiuse
  • 2,585
  • 1
  • 23
  • 31