Why do I prefer "Option :: ok_or_else" instead of "Option :: ok_or"?
I just saw the following change to the pull request:
- .ok_or(Error::new(ErrorKind::Other, "Decode error")); + .ok_or_else(|| Error::new(ErrorKind::Other, "Decode error"));
The only differences I know are:
- In
ok_or
we have already createdError
onError::new
and passed it to the adapter. - In
ok_or_else
we passed a closure that produces such a value, but it cannot be called ifOption
there isSome
data in.
Did I miss something?
source to share
The main reason for using ok_or_else
or any of the methods ..._or_else
is to avoid executing the function when it is not needed. Whether Option::ok_or_else
or Option::unwrap_or_else
not there is a need to run additional code if Option
- Some
. This may make the code faster, depending on what happens in the event of an error.
This example Error::new
is probably doing the highlighting, but it could also write to standard, make a network request, or whatever any piece of Rust code can do; it's hard to tell from the outside. It is generally safer to place this kind of code in a closure, so you don't have to worry about extraneous side effects when success happens.
Clippy is for you too:
warning: use of `unwrap_or` followed by a function call --> src/main.rs:3:15 | 3 | let foo = foo.unwrap_or("hello".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `foo.unwrap_or_else(|| "hello".to_string())` | = note: #[warn(or_fun_call)] on by default = help: for further information visit https://github.com/Manishearth/rust-clippy/wiki#or_fun_call
source to share
Signature std::io::Error::new
equals
fn new<E>(kind: ErrorKind, error: E) -> Error
where
E: Into<Box<Error + Send + Sync>>,
This means it Error::new(ErrorKind::Other, "Decode error")
is allocating memory on the heap because it error
needs to be converted to Box<Error + Send + Sync>
for use.
Therefore, this pull request removes unnecessary memory allocation / deallocation when value Result
is Result::Ok
.
source to share
In addition to performance implications, more complex arguments ok_or
can produce unexpected results if you are not careful enough; consider the following case:
fn main() {
let value: Option<usize> = Some(1);
let result = value.ok_or({ println!("value is not Some!"); 0 }); // actually, it is
assert_eq!(result, Ok(1)); // this holds, but "value is not Some!" was printed
}
This could have been avoided with ok_or_else
(and the same goes for other functions *_or_else
) since the closure is not evaluated if the option Some
.
source to share