rustanyhow

Rust compiler appears confused by Result type with anyhow


I'm trying to compile something like the following:

use anyhow::Result;
use std::sync::{mpsc, Mutex, OnceLock};
use std::cell::Cell;
use std::fmt;

static COMS: OnceLock<Mutex<Coms>> = OnceLock::new();

struct Coms {
    tx: Cell<Option<mpsc::Sender<Msg>>>,
    rx: Cell<Option<mpsc::Receiver<Msg>>>,
}

enum Msg {}

fn bar() -> Result<()> {
    let (tx, rx) = mpsc::channel();
    COMS.set(
        Mutex::new(
            Coms {
                tx: Cell::new(Some(tx)),
                rx: Cell::new(Some(rx)),
            }
        )
    );
    //)?;
    Ok(())
}

fn main() {
    let _ = bar();
}

I get an expected warning about an unused Result:

warning: unused `Result` that must be used
  --> src/main.rs:17:5
   |
17 | /     COMS.set(
18 | |         Mutex::new(
19 | |             Coms {
20 | |                 tx: Cell::new(Some(tx)),
...  |
23 | |         )
24 | |     );
   | |_____^

However, if I swap out the line with the ); for a )?;, I get an error like:

error[E0277]: the trait bound `Mutex<Coms>: std::error::Error` is not satisfied
  --> src/main.rs:25:6
   |
25 |     )?;
   |      ^ the trait `std::error::Error` is not implemented for `Mutex<Coms>`
   |

The documentation for OnceLock::set() says it returns a Result, so why does it look like it is returning a Mutex<Coms> in the second case?

Why does rustc seem to think this is a Result in the first case (expected) and a Mutex<Coms> in the second case? This is using rustc 1.71.0. I must be misunderstanding what the compiler is trying to tell me.


Solution

  • It is a Result. Specifically, the signature of that function is

    pub fn set(&self, value: T) -> Result<(), T>
    

    On success, we return (). On failure, we give ownership of the T back to the caller.

    In your case, the return type is Result<(), Mutex<Coms>>.

    Now, anyhow::Error is a really neat type. It's capable of storing any error condition, but not any type. It can't store an i32, or a lot of other random types. It can only store types that implement Error, and Mutex does not implement that trait.

    You can't convert a Mutex<Coms> into a anyhow::Error.

    You can explicitly discard it if you don't care about the error condition.

    let _ = COMS.set(...);
    

    This is a loud assertion to Rust saying "Yes, we might get Err. I don't care; ignore it".

    You can replace the Mutex<Coms> with an actual error type using map_err and then use ? on that to convert to anyhow::Error. You can panic on failed sets, using expect.

    Or, of course, you can just straight-up pattern match on the result and do something custom with the original mutex.