22

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.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Petrus Theron
  • 27,855
  • 36
  • 153
  • 287
  • Rust follow semver, breaking change would imply that Rust up the major version so if you really found a regression it's a bug. If you can create an [mcve] and specify both Rust version that show the regression, summit an issue, I didn't find any one about your case currently open. – Stargateur Dec 02 '18 at 21:55
  • 3
    What `Result` type are you using here? `std::result::Result` has always expected two types arguments, while `std::io::Result` for example only needs one. – SirDarius Dec 02 '18 at 22:08
  • 1
    And what is the `Err` type you're feeding into this `Result` in the last case? – Cerberus Dec 03 '18 at 03:27
  • I just hit this in the Rust Cookbook's Directory Traversal example: https://rust-lang-nursery.github.io/rust-cookbook/file/dir.html – hippietrail Nov 28 '20 at 09:30

4 Answers4

41

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).

E_net4
  • 27,810
  • 13
  • 101
  • 139
  • What an absolutely wonderful answer. I was really confused and this just gave me a sudden "lightbulb" moment, where I suddenly understood what's going on in a third party example code. – Andre Apr 21 '23 at 10:33
8

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(())
}
E-rich
  • 9,243
  • 11
  • 48
  • 79
4

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

Mikolasan
  • 626
  • 10
  • 19
-1

i solved my own error by making a generic Result type to handle the error

As its says it require a generic of T and E, so to simplify things, i had to follow this way

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

Gabriel soft
  • 432
  • 3
  • 7