Mutating an enum variant without copying


#1

I have a ringbuffer that I’m using to pass messages between 2 threads in a queue stylee. Some of the messages can be pretty big and so I’d like the sender to construct the message into the ringbuffer directly rather than separately on the stack/heap and then copy it into the ringbuffer.

I’d like to use an enum to represent a message on the ringbuffer, which can have many variants. e.g.

pub enum Message {
    A(u32),
    B(u32, [u8; 1024*1024]),
    ...
}

The question is how to construct a message of type ‘B’ into the ringbuffer without copying. The only way I can find to achieve this is something like:

pub fn set_b_message(slot: &mut Message) {
    // set the type tag without zeroing the data
    *slot = Message::B(0, unsafe{ ::std::mem::uninitialized() });
    if let &mut Message::B(_, ref mut buf) = slot {  // always matches
        generate_message_contents_into_buffer(buf);
    }
}

Which generates the assembler I’m looking for (assuming generate_message_contents_into_buffer doesn’t get inlined):

set_b_message::hefc441441e713372Uaa:
	mov	qword ptr [rdi], 1
	add	rdi, 8
	mov	esi, 1048576
	jmp	generate_message_contents_into_buffer::h5d2476d87bb1a7dfGaa@PLT

But the rust code feels a bit clunky because the ‘if let’ match is known to succeed at compile time.
Unfortunately the following results in a memcpy:

pub fn set_b_message2(slot: &mut Message) {
    let mut buf: [u8;1024*1024] = unsafe{ ::std::mem::uninitialized() };
    generate_message_contents_into_buffer(&mut buf);
    *slot = Message::B(0, buf);
}
set_b_message2::h3aeda2ec7f998ad7qba:
	push	r14
.Ltmp1:
	push	rbx
.Ltmp2:
	sub	rsp, 1048584
.Ltmp3:
.Ltmp4:
.Ltmp5:
	mov	rbx, rdi
	lea	r14, [rsp + 8]
	mov	esi, 1048576
	mov	rdi, r14
	call	generate_message_contents_into_buffer::hc756babb63c42709Gaa@PLT
	mov	qword ptr [rbx], 1
	add	rbx, 8
	mov	edx, 1048576
	mov	rdi, rbx
	mov	rsi, r14
	call	memcpy@PLT   <----- memcpy here
	add	rsp, 1048584
	pop	rbx
	pop	r14
	ret

Is there another way to achieve mutating an enum variant in-place without an if-let or match?

Thanks v much,

Phil