memcpy() comparison: C, Rust, and D

Michael Howell via Digitalmars-d digitalmars-d at puremagic.com
Wed Feb 1 09:28:28 PST 2017


On Wednesday, 1 February 2017 at 14:39:15 UTC, Cody Laeder wrote:
> [4] https://gist.github.com/1f34331b2cae6ba9e624c5f9f4f2a458

That example code won't even typecheck, and the minimal fix (make 
dest a slice) leaves it unsafe (T needs to be Copy). If you just 
want to quick bang out code like this, you should probably use 
play.rust-lang.org to make sure it works.

But, anyway, let's use the version of that function that's 
actually in the standard library: 
https://github.com/rust-lang/rust/blob/master/src/libcore/slice.rs#L531

// This trait is implemented for slices,
// so it can be invoked like this:
// dest.copy_from_slice(src)
pub trait SliceExt {
     type Item;

     // [other slice methods redacted]

     #[stable(feature = "copy_from_slice", since = "1.9.0")]
     fn copy_from_slice(&mut self, src: &[Self::Item]) where 
Self::Item: Copy;
}

To list the guarantees in the OP:

1. The signature doesn't say anything about side effects. This 
will probably be a const function, once those exist.

2. Since this function returns nothing, there is nothing to say 
about the return value. Because of how &mut pointers work in 
Rust, returning pointers like that is not ergonomic.

3. Nothing src points to, directly or transitively, can be 
mutated, unless T contains a cell (the compiler can and already 
does determine this on an as-needed basis, and a human reader can 
usually ignore interior mutability because it's used for 
semantically meaningless things like reference counts).

4. self and src can't be saved, because they don't outlive the 
function invocation. The items behind it can't be saved, either, 
because Self::Item is a generic that might not live long enough 
(that feels like cheating, though, because it only works for 
generics or if the data type is deliberately engineered to not be 
'static).

Unlike libc's memcpy (which is directly exposed, as part of the 
stable standard library as copy_nonoverlapping), the slice 
abstraction expresses that the length of the slice is within 
bounds of the underlying allocation. But D has slices, too, and 
probably has a version of this function, so that also feels like 
cheating.

This function signature *does* guarantee that src and self don't 
overlap, unlike the C and D versions. Personally, I think that's 
at least as important as whether the function's pure or not.

Here's a version of memcpy that's blatantly unidiomatic, but gets 
the same score on 1, 2, 3, and 4 as the slice version 
https://play.rust-lang.org/?gist=1f3a07987258500b8afd5a30e589457b:

unsafe fn copy_nonoverlapping_ref<T>(src: &T, dest: &mut T, len: 
usize) {
   std::ptr::copy_nonoverlapping(src, dest, len)
}

Again, it doesn't guarantee no side effects, it may guarantee 
that src isn't mutated, it does guarantee that they aren't stored 
away somewhere, and it guarantees that src and dest don't 
overlap. It's still unsafe, because it doesn't do anything about 
len being possibly out of bounds, and I left out the Copy bound 
for the sake of flexibility.


More information about the Digitalmars-d mailing list