Why `Option::is_some_and` takes ownership?

What was the rationale behind that design decision? Why isn't there an alternative method that borrows instead?

You can always add .as_ref() before methods that take ownership to make them work by reference.

6 Likes

To answer the other question, the rationale for the decision seems to be given in Change `is_some_and` to take by value by camsteffen · Pull Request #98354 · rust-lang/rust · GitHub

Consistent with other function-accepting Option methods.

The linked discussions in that ticket goes over the pros and cons, including references to the as_ref() workaround.

3 Likes

It also more general / sidesteps a potential future is_some_and_mut request. My contrived example.

(Didn't seem to come up in the conversation, so maybe not a huge use case...)

4 Likes

I want to clarify that I was concerned about the content of Some being moved out, but a simple (contrived) example shows why my concerns were unsustained:

fn f(){
    let v = vec![0];
    // BTW, I find it misleading that
    // the diagnostics suggest `clone`
    Some(v).is_some_and(|_| true);
    drop(v);
}

It doesn't compile, but there's a simple fix:

fn f(){
    let v = vec![0];
    Some(&v).is_some_and(|_| true);
    drop(v);
}

I'ts so obvious now that I wrote it: &T is Copy, so Option<&T> is Copy too! Therefore, no move occurs.

I've been learning Rust and its borrow-ck for months, and I thought that borrow would still be an error. I'm glad I proved myself wrong!

Edit: This doesn't compile either:

fn f(){
    let v = vec![0];
    // `(&Some(v)).as_ref()` doesn't work either
    Some(v).as_ref().is_some_and(|_| true);
    drop(v);
}

I remember seeing it referenced (indirectly) in this comment: https://github.com/rust-lang/rust/issues/93050#issuecomment-1166319932

Also consider &self is useless for a Option<&mut T> .

2 Likes

It's not even about Copy!

The move happens when you create the Option:

let v = vec![0];

let some_v = Some(v);

drop(v);

...will also move v into some_v and fail to compile.

You created an Option<Vec<i32>>, which, by definition, owns a Vec<i32> [1].

When you do the same with Option<&Vec<i32>>, it owns a &Vec<i32>, and you don't move out of v to create that.

as_ref helps with the case where you have a reference to an Option owning a value (&Option<T>), but you want an Option that you own, but which only has a reference to the value (Option<&T>). This is common when you have a reference to a struct that stores owned data in an Option. But it is not helpful in your case, because your Vec<i32> is not stored in an Option in the first place (it's stored in v).

If you had this, then you could use as_ref (this does compile):

let some_v = Some(vec![0]); //the `Vec<i32>` is stored in (owned by) an Option

let some_ref_v: Option<&Vec<i32>> = some_v.as_ref(); // desugars to (&some_v).as_ref();

drop(some_v);

  1. enum Option<T> {
        Some(T),
        //   ^--here
        None
    }
    
    ↩︎
4 Likes

@FZs has provided an explanation, but URLO is the forum for questions about non-compiling code and the like.

6 Likes