Open reem opened 8 years ago
Holy shit my brain what
"proof" that it's safe (you can write it with safe code):
pub fn write<T>(target: &mut T, value: T) {
mem::forget(mem::replace(target, value));
}
but this is less efficient since you read the old value - this might be highly undesirable for large structs, so the more efficient unsafe implementation should be used.
docs for ptr::write are not up to date: “Beyond accepting a raw pointer, this operation is unsafe because it does not drop the contents of dst.” (Edit: This is now fixed)
Yeah I suppose it's literally only unsafe because it derefs a raw pointer now.
Maybe call it overwrite
or erase_with
or something, to suggest that it’s different from *target = value
which runs the destructor of the previous value?
Can you provide a use case that isn't dealing with unsafe code anyway? I see no point in having a function of dubious value advertised within the standard library when almost all use cases will be either dealing with unsafe code anyway (and there is a perfectly fine function already for unsafe code, which is at least as ergonomic to use) or highly likely to be a bug. The only thing I can think of is overwriting large, POD structs. I do not find it a compelling use case myself, and it seems like something an optimizer should be able handle when doing just plain *target = val;
(I don't know if this is the case though).
Generally speaking I don't think there's a good reason for people to introduce a raw pointer dereference into their own code where there is no need for one. Even if the context is unsafe, you can still make your life a tiny bit easier by isolating the places where unsafety can originate from.
There are also the cases where you want to do this for pure efficiency reasons, and there's no reason to then introduce unsafe where none is really needed.
@reem &mut coerces to *mut so ptr::write already works for that. That's why it's a free fn.
@Gankro right, except ptr::write
is unsafe to call, and this function would not be. I personally prefer not to have to coerce to a raw pointer if I can stay in &
-land.
But of course mem::write
does coerce to a raw pointer internally. You can always have this one-liner function in your own crate.
I actually think this is a good pattern: write simple functions like this to tighten bounds on or "shape" of generic types. As opposed to e.g. using transmute
without type annotation, relying on inference, which could be dangerous if some signature elsewhere changes and causes the meaning of that transmute to change. Or in this case, the signature of this mem::write
function ensures that the raw pointer given to ptr::write
is valid.
The function doesn’t need to be in std
to achieve this.
It may not need to be in std
, but if it is a good pattern, why shouldn't it be included?
I think generally useful, if trivial, safe wrappers around unsafe
functions need a home somewhere, either in the stdlib or a well vetted or blessed external crate.
It's a big relief not having to have any unsafe
blocks in your crate because you don't have to spend brain cycles on that niggling worry about undefined behavior--regardless of how well founded those concerns actually are.
I think it's doing Rust's users and design goals a great disservice to say, "Yeah, it's a useful wrapper around an unsafe function, but it's trivial so just write it yourself."
It's also giving a bit of a mixed message because the community at large tends to discourage more complex solutions using unsafe
when a safe one exists, even when the latter is clunkier or less performant. You could argue that the difference between the two situations is the complexity of the solution itself, but that isn't immediately apparent.
A straightforward and not uncommon example is getting two mutable references to nonequal indices in a slice. Since slices don't provide this method directly, it's up to the user to figure out how to implement it or try to find a crate that's done it already. The split_at_mut()
solution is only one line longer than the unsafe
version in the way I implemented it, but it comes with a good bit of additional cognitive overhead. It also requires an extra comparison and branch which only exist to facilitate the workaround.
Now that
mem::forget
is safe, it would be useful to have a function like:probably in
std::mem
, alongsideswap
andreplace
.