Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

fn foo() -> Result<()> throws "expected 2 type arguments"

Why isn't Result<()> allowed when compiling this bit of Rust code? Is it a breaking change between Rust editions?

fn run() -> Result<()> {
    let (tx, rx) = channel();

    thread::spawn(move || {
        do_things_with_tx(&exit_tx);
    });

    match exit_rx.recv() {
        Ok(result) => if let Err(reason) = result {
            return Err(reason);
        },
        Err(e) => {
            return Err(e.into());
        },
    }

    Ok(())
}

The compiler says:

error[E0107]: wrong number of type arguments: expected 2, found 1
    --> src/main.rs:1000:18
     |
1000 | fn run_wifi() -> Result<()> {
     |                  ^^^^^^^^^^ expected 2 type arguments

When I tweak the return type to Result<(), Err>, it says:

error[E0107]: wrong number of type arguments: expected 2, found 0
    --> src/main.rs:1000:29
     |
1000 | fn run() -> Result<(), Err> {
     |                        ^^^ expected 2 type arguments

This is from the wifi-connect project.

like image 404
Petrus Theron Avatar asked Dec 02 '18 21:12

Petrus Theron


3 Answers

The definition of Result is, and has always been, the following:

pub enum Result<T, E> {
    Ok(T),
    Err(E),
}

This definition is even presented in the Rust Programming language, to show how simple it is. As a generic sum type of an OK outcome and an error outcome, it always expects two type parameters, and the compiler will complain if it cannot infer them, or the list of type arguments does not have the expected length.

On the other hand, one may find many libraries and respective docs showing a Result with a single type argument, as in Result<()>. What gives?

It's still no magic. By convention, libraries create type aliases for result types at the level of a crate or module. This works pretty well because it is common for those to produce errors of the same, locally created type.

pub type Result<T> = std::result::Result<T, Error>;

Or alternatively, a definition which can still purport as the original result type.

pub type Result<T, E = Error> = std::result::Result<T, E>;

This pattern is so common that some error helper crates such as error-chain, will automatically create a result alias type for each error declared. As such, if you are using a library that may or may not use error-chain, you are expected to assume that mentions of Result<T> are local type aliases to a domain-specific Result<T, Error>. In case of doubt, clicking on that type in the generated documentation pages will direct you to the concrete definition (in this case, the alias).

like image 175
E_net4 stands with Ukraine Avatar answered Oct 16 '22 21:10

E_net4 stands with Ukraine


From The Rust Programming Language section The ? Operator Can Only Be Used in Functions That Return Result

use std::error::Error;
use std::fs::File;

fn main() -> Result<(), Box<dyn Error>> {
    let f = File::open("hello.txt")?;

    Ok(())
}
like image 7
E-rich Avatar answered Oct 16 '22 20:10

E-rich


TL;DR

use std::io::Result;

Link to the type description

Long answer

I believe that the top-voted answer given by E_net4 the comment flagger is correct. But it doesn't work if applied blindly. In both cases

this

pub type Result<T> = Result<T, Error>;

and this

pub type Result<T, E = Error> = Result<T, E>;

will give the cycle dependency error

error[E0391]: cycle detected when expanding type alias `Result`
   --> src\main.rs:149:33
    |
149 | pub type Result<T, E = Error> = Result<T, E>;
    |                                 ^^^^^^^^^^^^
    |
    = note: ...which immediately requires expanding type alias `Result` again
    = note: type aliases cannot be recursive
    = help: consider using a struct, enum, or union instead to break the cycle
    = help: see <https://doc.rust-lang.org/reference/types.html#recursive-types> for more information

So as much as users of SO don't want to admit it, but Gabriel soft is very close to elegant solution, because that type alias

pub type Result<T> = result::Result<T, Error>;

is straight from the standard library.

Here it is, our desired Result with 1 generic argument is defined in std::io (docs). To fix the problem I added

use std::io::Result;

fn some_func() -> Result<()> {
  ...
}

or

use std::io;

fn some_func() -> io::Result<()> {
  ...
}

rustc 1.62.1

like image 3
Mikolasan Avatar answered Oct 16 '22 21:10

Mikolasan