Cannot move out of `X` because it is borrowed

I am trying to fix some rusty error,

I create this code snippet, to describe my intention, This is a simple view of original code

playground,

struct X;
enum A {
    X(X),
}
impl A {
    fn clear(self) {}
}
fn main() {
    let mut a = A::X(X);
    match a {
        A::X(ref mut x) => {
            let c = || {
                println!("{:?}", x);
                a.clear();
            };
            c();
        }
    }
}

Error:

error[E0505]: cannot move out of `a` because it is borrowed
  --> src/main.rs:14:21
   |
13 |         A::X(ref mut x) => {
   |              --------- borrow of `a.0` occurs here
14 |             let c = || {
   |                     ^^ move out of `a` occurs here
15 |                 println!("{:?}", x);
   |                                  - borrow later captured here by closure
16 |                 a.clear();
   |                 - move occurs due to use in closure

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

I do understand that what the error is about E0505, But I don't know How to fix it! There is some solution that use reference, But I can't use that, So I need to Copy, which Also can't do, So How can I fix it ?

If you visit into source code, you will saw this like is commented (// this.pages.write(page_no as u64, &node.to_bytes()).unwrap();)

If I uncomment this line, I would get this error message, Trying to fix it for 2 days, even When I work out to solve this error, It create 2 new error! I just need some way to tell rust complier Shut Up!

> I just need some way to tell rust complier *Shut Up!*

Telling someone to shut up when they're telling you that you're doing something wrong is usually a bad idea.


The toy example you provided can be fixed by removing the closure: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=c23cb13ea198646bc45599e09966b6ea

I'm not sure why you put the closure there in the first place, the github code contains no closure. I believe the println!() in your example also causes the compiler to move x, which makes things more complicated than just taking a mutable reference.

Indeed, There is a closure, right here! on line 64 If removing closure in right think to do, I would like to remove it, but I Don't like to copy paste same code twice!

Your code is UB due to your transmuting in node.rs. You need, at least, some #[repr(C)] various places and #[repr(u64)] on the enum. See the reference for more on specifying layout. (Even then you may have other problems, e.g. around uninitialized data if you ever convert the smaller Leafs.)

As for your question, you can't consume something you're borrowing from. So try matching by value and adjusting the match arms so they compile instead.

    match node {
        Node::Branch(branch) => {
            // ...
            let mut handler = |i, mut left: Branch| -> Result<Option<(u64, u16)>> {
                // ...
                    this.pages.write(page_no as u64, &Node::Branch(left).to_bytes()).unwrap();
                // ...
            }
            // ...
         }
        Node::Leaf(mut left) => {
            // ...
            this.pages.write(page_no as u64, &Node::Leaf(left).to_bytes())?;
            // ...
        }
    }
2 Likes

Thanks for those information's, I did not know that rust doesn't guarantee of there default data layout, So, "You need, at least, some #[repr(C)] various places and #[repr(u64)] on the enum" - repr(C) make sense, but why repr(u64) on enum ?

Context when I made my suggestion: I misread the definition of Leaf and thought it was definitely smaller than Branch. (See further down where I address this in more depth.) With that context in mind...

You can see in the reference that this will result in a layout equivalent to a #[repr(C)]-like union where the discriminant is a u64. I mentioned this because when I checked the size of your Branch, it was 4088, which is 8 bytes shy of the [u8; 4096] you're transmuting between -- thus the discriminant must be taking up 8 bytes. Hence, u64, an 8-byte value.


I took a closer look when writing this reply. Here are the sizes I see with your data structures as they are, and as you can see both Branch and Leaf are 4088 bytes (and Node is 4096 bytes, which is why your transmute can succeed). (Note that the discriminant might not actually be a u64, it could just be aligned in such a way that 8 more bytes are needed.)

If we just blindly make the non-enum structs #[repr(C)], though, Leaf grows in size to 4096 (and Node grows to 4104). What happened? Well, rustc was reordering the members of your Leaf data structure already in order to make it smaller. The alignments necessary in the given order force the larger size. The Node enum, which has to fit this data structure plus the discriminant, is necessarily larger.

Putting len at the end restores the 4088 length of Leaf, and then the #[repr(u64)] on Node results in a 4096 size again, but with guaranteed ordering. (But not the ordering in your original code -- which is not the ordering in the compiled binary, as witnessed by the difference in size.)

Edit: Thinking on it for a minute more, the size of the discriminant can be smaller than u64 and the alignment will push it up to 4096 anyway. So you have some flexibility here, if you care. You may want some assertions on the size(s) for robustness.

2 Likes

Note that enums with fields also need repr(C) for guaranteed layout. One can combine this with a repr specifying the discriminate type, e. g. repr(C, u64). I haven't read the previous discussion in enough detail to comment on or make suggestions on the choice of discriminate type. Edit: That's probably wrong and repr(u64) alone works, too; see @quinedot's answer below.

Note that transmuting to [u8; N] should also be UB if the structs contains any padding bytes, as those could be uninitialized, while u8 must not be uninitialized.

2 Likes

By my reading of the reference, this is incorrect (but I haven't tracked down a normative citation). If my reading is incorrect, can you elaborate? Quote (and example follows):

The representation of a primitive representation enum is a repr(C) union of repr(C) structs for each variant with a field. The first field of each struct in the union is the primitive representation version of the enum with all fields removed ("the tag") and the remaining fields are the fields of that variant.

(And the fieldless, primitive-specified discriminant enum has well-defined values.)

This is a very good point. You can replace padding with [u8; SIZE_OF_PADDING] and fill them with 0s or the like (e.g. in constructors).

1 Like

Interesting. Looks like I'm wrong than. I thought I remembered otherwise.

Edit, so in a second read of that reference, I come to the conclusion that either I must have misread something last time I looked at it, or something must have changed. Even if it's the latter, the reference could probably be extended by some section highlighting the differences and similarities between repr(C) vs repr(uN) vs repr(C, uN) on enums with fields.

1 Like

Thanks! steffahn And quinedot For you time,

"Note that transmuting to [u8; N] should also be UB if the structs contains any padding bytes" -

In my example every compound struct has padding! (expect Entry which doesn't contain any padding bytes), Is this why we need extra repr(u64) on enum Node ?

So How do I safely transmute between type T and bytes ? that has paddings?!

After reading this post, Safe Transmute, Which also give us an example ToFromBytes trait, that use transmute and slice::from_raw_parts, would be able to solve this issue ?

You need to figure out where the padding is (by looking at sizes and alignment) and fill it in with non-padding. Here I've done it somewhat manually and it's all at the end of the structs, but other orderings could require more than one padding array (between fields). A more rigorous approach would probably use align_of as well.

The bytemuck crate can assure you got it right (though I don't know if you actually want your data types to be Copy).

To avoid padding between the discriminant and the data, you'll want repr(u64) as well. (But note that the enum is not "Plain old data (Pod)" in the bytemuck sense, as the discriminant must always be 0 or 1. If you wanted to ensure this in your transmuting function, check the bytes before transmuting.)

1 Like

The UCG does a much better job of explaining both the situation and the motivations here! (TLDR: repr(C, primitive) is less efficient than repr(primitive) but better matches C++ enums.)

3 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.