Borrowing a derived Result results in unwanted &()

I have a function that calls another function that returns a Result<>, and I want to send just the error part, if there is one, to another function to log it. I run into an issue, as Rust seems to want to make a reference to the unit type along the way.

The code probably shows what I'm trying to do most clearly:




#[derive(Debug)]
enum Myerror {
    Field(String),
}

fn result() -> Result<String, Myerror> {
    Ok("foo".to_string())
}

fn borrower(v : Result<(), &Myerror>) {
    println!("{:?}", v);
}

fn user() -> Result<String, Myerror> {
    let res = result();
    let tmpres = match res {
        Ok(_) => Ok(()),
        Err(ref e) => Err(e),
    };
    borrower(tmpres);
    res
}

fn user2() -> Result<String, Myerror> {
    let res = result();
    let tmpres = match res {
        Ok(_) => Ok(()),
        Err(_) => Err(Myerror::Field("Fooey".to_string())),
    };
    borrower(tmpres.as_ref());
    res
}


(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
  --> src/lib.rs:33:14
   |
33 |     borrower(tmpres.as_ref());
   |     -------- ^^^^^^^^^^^^^^^ expected `()`, found `&()`
   |     |
   |     arguments to this function are incorrect
   |
   = note: expected enum `Result<(), &Myerror>`
              found enum `Result<&(), &Myerror>`
note: function defined here
  --> src/lib.rs:13:4
   |
13 | fn borrower(v : Result<(), &Myerror>) {
   |    ^^^^^^^^ ------------------------
help: use `Result::copied` to copy the value inside the `Result`
   |
33 |     borrower(tmpres.as_ref().copied());
   |                             +++++++++

For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground` due to previous error

How can I make this work without resorting to copying? Does the borrow checker really need to care about &()?

Yes, borrow checking and type checking will absolutely care about every last reference and fight you to death about it, even if it's a reference to a meaningless type you never use.

If you want the borrower to only look at the result, then you should pass &Result<…, …> to the function instead. Then it doesn't matter whether it's copyable or not.

If you really want Result<Copy, Reference>, then I think result.as_ref().map(|ok| *ok) is the least verbose way of doing that, or result.as_ref().map(drop) for the Ok(()) case.

If your borrower doesn't care about the Ok part, then you could make it fn borrower<Whatever>(result: Result<Whatever, &MyError>), and then it'd accept &() too.

But &Result is the best solution.

1 Like

The problem with the &Result<...,...> solution is that it doesn't support both the user1 use case where we reference the Myerror from the return value (Result<(),&Myerror>) versus the user2 use case where we construct a new err (Result<(), Myerror>).

To accomodate the &Myerror, I think I need to hoist the 'new' error I use in user2 like so:


fn borrower(v : Result<(), &Myerror>) {
    println!("{:?}", v);
}

fn user() -> Result<String, Myerror> {
    let res = result();
    let tmpres = match res {
        Ok(_) => Ok(()),
        Err(ref e) => Err(e),
    };
    borrower(tmpres);
    res
}

fn user2() -> Result<String, Myerror> {
    let res = result();
    let err =  Myerror::Field("Fooey".to_string());
    let tmpres = match res {
        Ok(_) => Ok(()),
        Err(_) => Err(&err),
    };
    borrower(tmpres);
    res
}

It does? In both cases it'd be borrower(&res).

It's very unlikely that you'd ever use Result<(), &E> for real-world cases, because temporary references to newly-created values can't be returned from functions (don't confuse temporary loans &, with returning "by reference" which is done with Box! Result<(), Box<dyn Error>> is common, but that's not the same as & reference.)

If you really must support both owned and borrowed errors, then this would work:

fn borrower<E: AsRef<MyError>>(v : &Result<(), E>) {
3 Likes

Note that calling .copied() on a Result<&(), &Myerror> does not make a copy of the error: it only "copies" the zero-size (), which does nothing but discard the address, turning it into a Result<(), &Myerror>.

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.