Use of moved value error with Box disappears if inside a method

Hello,

Unfortunately I am not allowed to share my original code, but I think I was able to reproduce the exact same behavior in the Rust playground.

Given a struct with two members not implementing Copy, I'd like to call a method on the first member taking self and the second member as parameters (so basically "consuming" both).

This is working fine, as you can see from the example below.

In my original code I have to box the struct (I'm dealing with FFI) and as soon as I do that, the compiler shows a "use of moved value" error. Deref-ing the boxed value doesn't work, too.

Since I am a new user and I can only attach two links to a post, all the above scenarios are here: Rust Playground

If I move the logic inside a method of the struct taking self as parameter, the error disappears: Rust Playground

I have read that methods in Rust are nothing more than syntactic sugar on top of functions, but this doesn't look like it.

I am not sure whether this is bug, a limitation of the compiler or just the lacking of my understanding of Rust nuances. Any insight would be much appreciated.

Cheers,
Antonio

You can take the value of the box and then do partial moves from there:

let ab = *boxed_ab; // take the AB value out of the box - box is consumed
ab.ma.consume(ab.mb); // Partial moves/consumes of the AB struct are OK - Rust understands this on structs

Ah, wow, I didn't think about that! :stuck_out_tongue:
So the problem is that the compiler is not smart about boxes?

I think it's just the compiler doesn't know that you intend to move the entire AB out of the box and there's an ordering of operations involved there. For example, say consume panics - you may be left with a box owning a partially moved out value. I think it's somewhat similar to cases where the compiler cannot tell that you're moving a value out of something borrowed mutably but also replacing it with something, e.g.:

struct NoCopy {}
struct S { v: NoCopy }

fn replace(s: &mut S) {
    let prev = s.v;
    s.v = NoCopy {}; // does not work - must use std::mem::replace instead
}

When you have a plain struct, Rust understands that its fields do not overlap with each other (i.e. they're disjoint). As such, you can do partial moves out of it, taking out a field at a time:

#[derive(Debug)]
struct NoCopy {}

#[derive(Debug)]
struct S {
    v: NoCopy,
    u: NoCopy
}

fn replace(mut s: S) {
    let old_v = s.v;
    // println!("{:?}", s); <-- wouldn't work - cannot use a partially moved-from value
    s.v = NoCopy{}; // restore `s`
    println!("{:?}", s); // ok because s is "whole" again
    let old_u = s.u;
}

Going back to your example, your works(self) method is essentially acting as the let ab = *boxed_ab bit of code that takes the value out of the box.

1 Like