How can I deep copy an Arc?


#1

Hello,
In an application I’m developing, I have a recursive enum, with the child of each enum “node” being wrapped in an Arc. To parse that enum, I “peel” it, removing the layers of an enum - so:

TheEnum {
Arc {
TheEnum {
Arc {
TheEnum {
data : 20,
}
}
}
}
}
would become (after 1 “peeling”):
TheEnum {
Arc {
TheEnum {
data : 20,
}
}
}

Obviously, the above is an extreme simplification of what I’m doing - TheEnum contains more information than just the arc of it’s child. Apparently Box does not work (I’ll try again, but I don’t think it does, I’ve tried it before). However, I have to “peel” the enum to different levels many times (for example, if the user inputs “2”, the outer 2 layers are removed, if the user inputs 3, the outer 3 are removed, etc). Repeatedly peeling to different degrees requires that you reset the enum that you’re going to peel back to the original (if you’ve already peeled the enum 3 times and the user requests it peeled only 2 times, you’re screwed, because each “peeling” already removes the information in the enum above it). However, trying to clone() the enum only increases the refcount, which then makes the try_unwrap function for Arc fail. Any ideas on how to deep clone a recursive enum containing an Arc?

If the above was confusing, yeah, it is, I couldn’t find a good way to explain it. If you’re confused just ask, I’ll do my best to answer.

Thanks


#2

If you have an arc: Arc<T> where T: Clone then you can do T::clone(&arc) to do a deep clone. If T: Copy simply dereference the arc like so *arc.


#3

Note that the example contained an Arc inside, too. So I’d call that doing an inner clone, not a deep one.


#4

Arc::make_mut() will clone if necessary.


#5

Will it increase the refcount?


#6

Or even better, is there a way to avoid cloneing the structure in the first place (for example, “peel” the enum without actually making any modifications to the original enum)?


#7

Will the clone automatically clone the whole recursive datastructure (so that every single Arc underneath the node has a refcount of 1)?


#8

make_mut gives you Arc with refcount == 1. It’ll clone and make a new Arc if refcount was > 1, or do nothing if recount was == 1.


#9

Rust’s Clone is generally shallow, but it depends on data types. Clone of Arc only increases refcount. Clone of enum runs (shallow) clone on its content.


#10

Is there any way to explicitly and always clone and return an Arc with refcount ==1?

Thanks!


#11

You could, but that’s equivalent of not having Arc at all. If you don’t want refcount greater than 1, then use Box.


#12

Aah, yes. Thank you! Probably has better performance, anyways.

If I do use box (I think it will work), is there a way to avoid actually cloning the enum each time I “peel” it to a different level?


#13

Yes, if you dereference a Box you get the value in the Box.

fn main() {
    struct Foo;
    let boxed_thing = Box::new(Foo);
    let unboxed_thing: Foo = *boxed_thing; // type annotation is not necessary
    println!("{}", unboxed_thing);
}

Which works by compiler magic surrounding Box.


#14

Yeah, the compiler magic has always annoyed me lmao.


#15

Just remember that in pre 1.0 Rust, box was a special sigil. It’s always had magic treatment.


#16

Using “box” is going along quite well right now, however, there’s one issue - is there an equivalent to a Weak pointer for Box? Right now I’m just returning a reference to the Box (I used to return a weak pointer to the arc), which is causing lifetime issues.


#17

What’s supposed to happen when you upgrade this weak pointer?


#18

er… that’s the issue lmao


#19

Wow, it’s amazing how many errors occur if you switch from Arc to Box (at NO POINT does any Arc I use have more than 1 strong reference, yet it is NOT going smootly)


#20

I wonder why do you place so much importance on keeping refcount == 1. Bumping of refcount is quite cheap — very likely much much cheaper than full clones of the content. There’s no extra cost to having higher refcount. Arc doesn’t even have into_inner(), so it shouldn’t make any difference to you.

And there’s no weak pointer for Box. If you need backreferences, then you’ll need Arc. Box can be referenced with regular Rust references, but these are strictly “strong”.