Why does this unsafe code fail?

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=b189adef179b298c1d3d1acd8a5225a1

Hi, truncate will drop the elements in src then dst will try to drop them a second time when it goes out of scope.

From truncate doc:

Shortens the vector, keeping the first len elements and dropping the rest.

I would use set_len for src too.

Also you might not know but you can run miri directly in the playground and it will point at the issue.

1 Like

The failure happens because using copy_nonoverlapping to copy a String yields two strings with a pointer to the same allocation, which causes a failure when you drop the string for a second time. The first drop happens in the call to truncate, the second call happens at the end of the loop when the local variables go out of scope and get dropped.

2 Likes

I suspect you're looking for

fn append<T>(src: &mut Vec<T>, dst: &mut Vec<T>, i: usize) {
    dst.extend(src.drain(i..));
}
2 Likes

Thank you for the help. Indeed, the code works after replacing truncate with set_len.
Unfortunately, performance against safe append is almost the same - but that's another story.

I mean, that's a good thing, no? If doing it safely is about as performant as doing it unsafely, that means there's no reason to use unsafe here.

5 Likes

Sure, that's a good thing. That means the compiler does its optimization job really well. I merely had a hope that by eliminating intermediate allocation I could get a better perf.

The context is that I'm trying to optimize the run time of a stack-based virtual machine by reducing memory movement and de/allocations, among other things. The same VM implemented in C++ is several times faster (3-4x).

The dst.extend(src.drain(i..)) solution also eliminates the intermediate allocation.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.