3

I have a mutable reference to a container, and I want to modify one of its interior values:

struct BigContainer {
    contained: BigUint,
}

fn double(container: &mut BigContainer) {
    container.contained = container.contained * 2_usize;
}

BigUint doesn't implement MulAssign, so we can't just use the *= operator. Consequently, the code as written fails, because Mul takes ownership of the value; we need to temporarily move out of borrowed content. (playground)

We can get around that by initializing a temporary value, and using std::mem::replace: (playground)

fn double(container: &mut BigContainer) {
    let prev_value = mem::replace(&mut container.contained, 0_usize.into());
    container.contained = prev_value * 2_usize;
}

This works, but it's a bit ugly, and we need to create that pointless filler value to temporarily hold the position open for us. What I want is some unsafe method which lets me just override the borrow checker. Unfortunately, the following does not work: (playground)

fn double(container: &mut BigContainer) {
    unsafe {
        container.contained = container.contained * 2_usize;
    }
}

The unsafe block does nothing for us in this case. Is there any way to accomplish this, safe or not, which eliminates the temporary value allocation? If not, is the compiler at least smart enough to skip it?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
coriolinus
  • 879
  • 2
  • 8
  • 18
  • 1
    You could always just help [fix the underlying code to allow what you want without using any unsafe code](https://github.com/rust-num/num/pull/330). – Shepmaster Sep 16 '17 at 19:52
  • 1
    *I want to modify one of its interior values* — That already has a name in Rust, called [interior mutability](https://doc.rust-lang.org/std/cell/index.html), but that's not what you want. You want to move out of a reference, which leaves the structure in an invalid state, causing problems on panic unwinding. – Shepmaster Sep 16 '17 at 19:55
  • 4
    You can just pass value by reference: `container.contained = &container.contained * 2_usize;` – red75prime Sep 16 '17 at 19:58
  • 2
    [Applying the duplicate's solution to your problem](https://play.rust-lang.org/?gist=fcd37126aed717628035d07b8ef43d6f&version=stable). – Shepmaster Sep 16 '17 at 20:00
  • @Shepmaster thanks! I see how that works; it's what I wanted. – coriolinus Sep 16 '17 at 20:26
  • @red75prime Huh. I see that your method works, but I don't understand why that works. Is there some property or trait of `BigUint` which makes this possible? Normally I wouldn't expect multiplying a reference by a value to work at all. – coriolinus Sep 16 '17 at 20:35
  • 2
    @coriolinus to be clear, the duplicate answers your **question**, but [red75prime's comment](https://stackoverflow.com/questions/46257713/is-there-some-unsafe-way-to-take-ownership-of-a-contained-value-in-order-to-muta?noredirect=1#comment79479332_46257713) **solves your problem** and is what you should use here. It works because there [are implementations for references](https://docs.rs/num-bigint/0.1.40/num_bigint/struct.BigUint.html): `impl<'a> Mul<&'a BigUint> for BigUint`. That's why the traits take values to start with — a reference is a type of value. – Shepmaster Sep 16 '17 at 20:35
  • @Shepmaster I'd like to add an answer here outlining the three techniques we've discussed, but I can't with it closed. Can you open it back up for me to do that? – coriolinus Sep 17 '17 at 10:41
  • red75prime's comment isn't an answer for *this* question which has a very broad topic (*Is there some unsafe way to take ownership of a contained value in order to mutate it*), but it would be an appropriate answer for a different question that was specifically about `BigUint`. You could ask a new question specifically dedicated to that problem. – Shepmaster Sep 17 '17 at 13:34

0 Answers0