Cannot move error for `Option<>`

hey guys I am having some issues over here with egui:

I have this variable:

let mut flameobject_blueprints: Option<blue_flame_common::structures::flameobject::Settings> = None;

And as soon as I reach over here:

        egui_plugin.ui(|ctx|
        {

I get this error:

however if I have this as my variable instead:

let mut flameobject_blueprints = blue_flame_common::structures::flameobject::Settings::init(None);

Then this is fine.

Not too sure as to why I have this issue when I wrap it under Option<>?

You'll probably get better help if you include

  • The entire output of cargo check (not the IDE-mangled version)
  • As text and not an image
  • With the entire portion of relevant code (which is probably the closure body)
2 Likes

So this is the error:

663  |           egui_plugin.ui(|ctx|
     |                          ^^^^^ `flameobject_blueprints` is moved here
...
1207 |                                       println!("{:?}", &flameobject_blueprints.unwrap());
     |                                                         ----------------------
     |                                                         |
     |                                                         variable moved due to use in closure
     |                                                         move occurs because `flameobject_blueprints` has type `std::option::Option<blue_flame_common::structures::flameobject::Settings>`, which does not implement the `Copy` trait

This is the variable being declared (outside the:

        // ui function will provide the context
        egui_plugin.ui(|ctx|
        {

)
loop

outside this loop this is how the variable is being delcared:

let mut flameobject_blueprints: Option<blue_flame_common::structures::flameobject::Settings> = None;

I guess as the error suggests I should implement a Copy trait.

Except you can't:

545 |     #[derive(Copy)]
    |     ^^^^^^^^^^^^^^^ not applicable here
546 |     let mut flameobject_blueprints: Option<blue_flame_common::structures::flameobject::Settings> = None;
    |     ---------------------------------------------------------------------------------------------------- not a `struct`, `enum` or `union`

So yeah not sure?

It's a common misunderstanding that error messages that point out a type does not implement the `Copy` trait would imply the suggestion that one should/could implement the Copy trait for the type in question. That is not the point of such an error message, and in most cases not a viable approach to solve the issue. The compiler is merely being complete in explaining where and why exactly the move operation happens that the main error message talks about. The proper approach is generally is to avoid the move operation by changing the code where it happens, not to avoid the move by implementing Copy for some type.


Now to address how to actually solve the problem at hand:

For unwrapping &Option<T> into &T, you must insert a call to the as_ref(self: &Option<T>) -> Option<&T> method, the result of which you can then unwrap().

1 Like

Makes perfect sense regarding the error message.

sorry but how would I exactly do this?

If it is such a common misunderstanding: I wonder to what extent it would be possible to add additional (possibly context sensitive) hints to the error message? Rust does that in many other places after all.

E.g: consider using .clone() or .as_ref() (depending on which of those traits are relevant/implemented).

1 Like

Ok so doing this &flameobject_blueprints.as_ref().unwrap() works, however I don't fully understand something,

&flameobject_blueprints.unwrap() isn't this already a reference as it is behind an &?

The problem is not with the error message; the error message is correct. The problem is that "does not implement Copy" is inherently ambiguous. It's possible that:

  • the calling code is in the same crate as the type, and the author forgot to add a Copy impl, in which case it can be the correct solution; or
  • the calling code is in a different crate, and thus a Copy impl is not possible, or it wouldn't even be the right solution (like it isn't in this case).

Unfortunately, the compiler can't really tell which one is the right solution, so I would advise against trying to come up with further suggestions, which could potentially induce even more confusion.

I guess we should instead teach people that "X does not implement Y" doesn't imply that they must inevitably shove an impl Y for X into their code.

It’s true that the type of &flameobject_blueprints.unwrap() is also &Settings. (Let’s call blue_flame_common::structures::flameobject::Settings just Settings to be concise.) That’s why you aren’t seening a mismatched types error. The error has nothing to do with whether or not “&flameobject_blueprints.unwrap() is a reference”.

Instead, well let’s start with what Rust is telling us. The full error message (which by the way you did not cite completely) probably looks something like

error[E0507]: cannot move out of `flameobject_blueprints`, a captured variable in an `FnMut` closure
 --> src/…
663  |           egui_plugin.ui(|ctx|
     |                          ^^^^^ `flameobject_blueprints` is moved here
...
1207 |                                       println!("{:?}", &flameobject_blueprints.unwrap());
     |                                                         ----------------------
     |                                                         |
     |                                                         variable moved due to use in closure
     |                                                         move occurs because `flameobject_blueprints` has type `std::option::Option<blue_flame_common::structures::flameobject::Settings>`, which does not implement the `Copy` trait

For more information about this error, try `rustc --explain E0507`.

I’d still be interested in seeing the actual full error message, and perhaps also what the Rust compiler version you were using is, and what the full relevant part of the source code looks like… since my attempt to create a similar error message resulted in far more useful results such as to following which already comes with the corresponding hint to add an as_ref() call.

error[E0507]: cannot move out of `flameobject_blueprints`, a captured variable in an `FnMut` closure
  --> <source>:12:27
   |
4  |     let flameobject_blueprints;
   |         ---------------------- captured outer variable
...
9  |     egui_plugin.ui(|ctx| {
   |                    ----- captured by this `FnMut` closure
...
12 |         println!("{:?}", &flameobject_blueprints.unwrap());
   |                           ^^^^^^^^^^^^^^^^^^^^^^ -------- `flameobject_blueprints` moved due to this method call
   |                           |
   |                           help: consider calling `.as_ref()` or `.as_mut()` to borrow the type's contents
   |                           move occurs because `flameobject_blueprints` has type `Option<Settings>`, which does not implement the `Copy` trait
   |
note: `Option::<T>::unwrap` takes ownership of the receiver `self`, which moves `flameobject_blueprints`
  --> /rustc/8ede3aae28fe6e4d52b38157d7bfe0d3bceef225/library/core/src/option.rs:932:25

error: aborting due to previous error

For more information about this error, try `rustc --explain E0507`.

In any case, as a first step of understanding what the error is, we should probably not be trying to re-invent the wheel in a forum, but look at the hint the compiler gives us (even for your compiler output, this part will be there): For more information about this error, try `rustc --explain E0507` .

Running this (or visiting the corresponding online version) gives useful information:

For example, there’s the information that implementing Copy is not the only possible approach to resolve the problem

To fix this error, you have three choices:

  • Try to avoid moving the variable.
  • Somehow reclaim the ownership.
  • Implement the `Copy' trait on the type.

There’s also the acknowledgement (though no examples) for that this can happen when closures are involved

This can also happen when using a type implementing Fn or FnMut, as neither allows moving out of them (they usually represent closures which can be called more than once). Much of the text following applies equally well to non-FnOnce closure bodies.

And in the end, there’s also a good link to more general learning resources about ownership and borrowing

For more information on Rust's ownership system, take a look at the References & Borrowing section of the Book.


Now with this out of the way, let’s address the concrete error at hand: The problem lies in unwrap which is a fn(self) method of Option, expecting an argument by-value (i.e. by move, for non-Copy types). Calling flameobject_blueprints.unwrap() in the closure that does not have ownership of flameobject_blueprints will result in an error.

Writing &flameobject_blueprints.unwrap() will not change anything about this.

First note the precedence of the operators: This expression means &(flameobject_blueprints.unwrap()), not (&flameobject_blueprints).unwrap(), so we are still trying to pass flameobject_blueprints by-value (i.e. moving its ownership) to the unwrap call, even if the result of that call is then only accessed by-reference.

Secondly, note that unwrapping an Option by-reference, is a common thing to do, but does not have its own method. Instead the combination of as_ref (or as_mut) with unwrap is used. This works fine because as_ref is a fn(&self) method of Option, not an fn(self) one. The result is a new Option, this time of type Option<&Settings>, which you can then, subsequently, unwrap by-value if you want to.

Note that the & you still have is redundant now. Writing &flameobject_blueprints.as_ref().unwrap() will create a reference to a reference, of type &&Settings, which can then be implicitly coerced back into a &Settings reference, which is why the additional & creates no issues here, but writing just flameobject_blueprints.as_ref().unwrap() should be enough.

4 Likes