-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Safe memcpy for slices ([T]::copy_from_slice) #1419
Conversation
Looks good to me. I'd personally prefer only panicking on |
I think it's a good call to have a free function for |
@Aatch I also don't feel too strongly about this, but the current design does make this specific use nice: // let src: &[T];
let mut dst: [T; N] = unsafe { std::mem::uninitialized() };
std::slice::copy(src, &mut dst); // You know that dst is fully initialized from here on |
As a note, dlang has special syntax for set and copy, and it only allows arrays of equal size. e.g. auto x = new int[10];
auto y = new int[11];
x[] = 5; // fill x with 5's
y[] = x[]; // Array lengths don't match for copy: 10 != 11 |
However, `std::slice::bytes::copy_memory`, the function I'm basing this on, only | ||
panics if `dst.len() < src.len()`. So... room for discussion, here. | ||
|
||
These are necessary functions, in the opinion of the author. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They could be methods on slices, not necessarily free functions.
I think |
@Amanieu I definitely prefer |
@eddyb @Amanieu I don't think there are issues around adding methods to slice types, but I don't personally like having these functions as methods. These feel "right" as free functions (at least, Although, perhaps |
@ubsan Keep in mind that the main reason for I can see how |
@eddyb true, for |
specifically about being defined for slices with uninitialized values Also add some language about alternatives
Add one function to `std::slice`. | ||
|
||
```rust | ||
pub fn copy<T: Copy>(src: &[T], dst: &mut [T]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this cause issue when T contains &U? We have to be careful to avoid lifetime lengthening. But I guess variance will fix it.
Note that free functions are harder to discover doc-wise. The general rust community seems to avoid them in code; though I'm not certain of this. |
@Manishearth slice.copy(slice2); // which is src? which is dst?
slice.copy_to(slice2); // okay, it's obvious here, but it's kind of ugly
slice.copy_from(slice2); // same as ^
std::slice::copy(slice, slice2); // nice! also, a connection to std::ptr::copy_nonoverlapping here. |
I think |
@ubsan equally to your first example: std::slice::copy(slice, slice2); // which is src? which is dst? I think this is what @Manishearth was talking about when he mentioned looking up the docs. It's obvious if you've used memcpy for years but for people new to systems programming it will be confusing. |
Not even that, because that convention isn't uniformly followed across programming languages. I think even rust had a deliberately "backwards" memcpy ordering in the past (I think it got fixed in the rush for 1.0 or something) |
The order proposed in the RFC is the opposite of void *memcpy(void *restrict dst, const void *restrict src, size_t n); Anyway, in many cases the Rust would look more like: std::slice::copy(&slice, &mut slice2); which makes it pretty clear which argument is source and which is destination. |
``` | ||
|
||
`fill` loops through slice, setting each member to value. This will lower to a | ||
memset in all possible cases. It is defined to call `fill` on a slice which has |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it might be worth mentioning how the behavior is defined :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"defined" meaning "will not exhibit undefined behavior". :P
@Manishearth @tomjakubowski I polled some people in the rust community, and it seems that generally people don't like my free functions, and they've convinced me, so I've decided to switch to methods. This also means that I've switched to the name |
Some alternatives that might be worth considering:
a[..] = b[..]; This is currently disallowed because [T] is an unsized type.
iter::copy(b.iter(), a.iter_mut());
iter::copy(b.iter().map(|x| x * 2), a.iter_mut()); The downside is that this might be getting too generic, and it wouldn't make sense to have the proposed panic semantics when the iterator lengths don't match. |
As far as iterators go there's not much value (relatively) there because you can write this as a function already. The reason this RfC is important is that currently there's no way to copy/fill slices in safe Rust efficiently. You have to use a for loop and hope the optimizer magicks it. This RfC provides a way to do that. For iterators, a one-line for loop just works and can't be optimized further, so there's much less value there. |
@ubsan mostly looks good, 👍 for this rfc. Small nit: Perhaps |
@Manishearth Hmm, I can see your argument... I do like @eddyb's suggestion of |
The naive version of the "clone_from_slice" loop produces terrible code and the naive fill loop produces good code. It's good that we have clone_from_slice so that the right way to tickle llvm for an optimized copying is abstracted out into a method. I don't think the pragmatic argument is the only one for clone_from_slice though. I think .fill(x) is a nice API, but it's not as important as .copy_from(). |
Agreed, it isn't as important. The only reason I also added |
In the case that |
@alexcrichton I would be fine with that. I don't personally see any pressing need for it, and no one else seems to see any pressing need for it. |
Yeah, I'm taking out |
👍 to this RFC. Guaranteed |
🔔 This RFC is now entering its week-long final comment period 🔔 My personal opinion is that this is good to go with the name |
@alexcrichton I don't personally like the name, because it's a very common function (at least in my personal code) to have such a long name. However, I do see the benefit of it in terms of consistency, so I am not opposed to it. I'd just prefer a shorter name. |
Let's get this in. Thanks for pushing it @ubsan (and I like your new avatar). Using this to replace most uses of |
@bluss thanks :) I needed a new avatar very badly... Yeah, I will be interested in io::Write performance on Debug (I think it uses |
A couple thoughts:
|
The libs team discussed this RFC during triage yesterday, and the conclusion was to merge this RFC. We discussed the possibility of having Anyway, I shall merge soon. Thanks again for the RFC @ubsan! |
implements rust-lang/rfcs#1419 r? alexcrichton
FWIW at this point, I'd most definitely prefer the shorter name |
…r=Mark-Simulacrum Add the "memcpy" doc alias to slice::copy_from_slice [RFC1419](rust-lang/rfcs#1419) describes `slice::copy_from_slice` as a "safe memcpy". This enables people searching for `memcpy` to find the `slice::copy_from_slice` method. Thanks! ## Screenshots This is currently the output when searching for "memcpy" -- `copy_from_slice` is safe, and should be part of this list. ![Screenshot_2020-11-19 Results for memcpy - Rust](https://user-images.githubusercontent.com/2467194/99722964-c9e8fe80-2ab1-11eb-82a5-4afe703a0eea.png)
No description provided.