ynimous
ynimous

Reputation: 5132

Merge boxed elements

Assuming that the following function exists:

fn merge(a1: A, a2: A) -> A { /* implemented */ }

Is there a way to write the function for the same operation using boxed types? That is:

fn boxed_merge(a1: Box<A>, a2: Box<A>) -> Box<A> { /* ??? */ }

Upvotes: 0

Views: 156

Answers (3)

Shepmaster
Shepmaster

Reputation: 430673

I would advocate that a by-value merge should not be your base implementation. Instead, a mutating method provides more flexibility. You can then build on that for by-value and by-boxed-value methods:

struct A(u8);

impl A {
    fn merge_ref(&mut self, other: &A) {
        self.0 += other.0
    }
}

fn merge(mut a1: A, a2: A) -> A { 
    a1.merge_ref(&a2);
    a1
}

fn boxed_merge(mut a1: Box<A>, a2: Box<A>) -> Box<A> {
    a1.merge_ref(&a2);
    a1
}

fn main() {
    let a1 = A(1);
    let a2 = A(2);

    let a3 = merge(a1, a2);

    let boxed_a3 = Box::new(a3);
    let boxed_a4 = Box::new(A(4));

    let boxed_a7 = boxed_merge(boxed_a3, boxed_a4);

    println!("{}", boxed_a7.0);
}

Notably, this will be more efficient in the boxed case as you don't have to perform any additional allocations.

As oli_obk - ker points out:

This only applies for merges of Copy structures. If you had two sets that are merged, the elements of the set might not be copyable

That could be addressed by taking the thing to merge in by value in merge_ref and using the same trick to move out of the box in the boxed_merge method:

struct B(Vec<u8>);

impl B {
    fn merge_ref(&mut self, other: B) {
        self.0.extend(other.0)
    }
}

fn merge(mut b1: B, b2: B) -> B { 
    b1.merge_ref(b2);
    b1
}

fn boxed_merge(mut b1: Box<B>, b2: Box<B>) -> Box<B> {
    b1.merge_ref(*b2);
    b1
}

fn main() {
    let b1 = B(vec![1]);
    let b2 = B(vec![2]);

    let b3 = merge(b1, b2);

    let boxed_b3 = Box::new(b3);
    let boxed_b4 = Box::new(B(vec![4]));

    let boxed_b7 = boxed_merge(boxed_b3, boxed_b4);

    println!("{:?}", boxed_b7.0);
}

We still don't have any extra allocation here.

Upvotes: 4

oli_obk
oli_obk

Reputation: 31173

You can re-use your merge function, by dereferencing the boxes inside the boxed_merge function. Note that this will not re-use the allocations but instead create a new one

fn boxed_merge(a1: Box<A>, a2: Box<A>) -> Box<A> {
    Box::new(merge(*a1, *a2))
}

Upvotes: 3

ljedrz
ljedrz

Reputation: 22173

You can access the contents of boxed arguments by dereferencing them and combine them in a new Box. For example:

fn merge(a1: Box<usize>, a2: Box<usize>) -> Box<usize> {
    Box::new(*a1 + *a2)
}

fn main() {
    let a1 = Box::new(1);
    let a2 = Box::new(2);

    println!("{}", merge(a1, a2));
}

Upvotes: 3

Related Questions