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
}
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.
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'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:
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>.