@Shnatsel I'd be very interested in doing a kind of "review" of the patterns you're seeing that people have to "roll their own"
Cool, I guess I need to sit down and make a list then! Or better, enable other people to make it themselves.
One was coming up so frequently I made an RFC to fix it: https://github.com/rust-lang/rfcs/pull/2714
oh dear god I remember that now
I'd like to see that move forward.
I was just saying to @Josh Triplett randomly that I think there is a lot of value in enabling people to write "things that used to need unsafe" but without unsafe
this is a point that @Ryan Levick impressed upon me when we were discussing #project-safe-transmute
but I think that this is going to ultimately mean 80% abstractions that can't cover all the cases
I guess that this method fits in that category
Yeah, we can't cover all the cases but we can gradually cover more and more of them.
from_le_bytes and friends on integers made a huge difference, as did TryFrom stabilization because it enabled feeding subslices to
Yeah. Though that reminds me...
I would really love to have a trait for
Another thing that's actually landing in 1.43 and came out of safety-dance is creating a
CString from a
Vec<NonZeroU8> without scanning the data twice. This enables constructing a
CString from a reader without resorting to
unsafe to avoid the rescan.
The first scan can transmute to note that it's non-zero?
Basically you read bytes from a reader until you reach 0, and since you're checking the bytes one by one anyway you might as well construct a
NonZeroU8 out of them right away as you put them in a
Vec. Then you can get a
CString from that
Vec without a second scan.
Given const generics, how hard would it be to specify a trait
ToEndianBytes that provides
to_le_bytes for a type, returning a slice of
u8 where the size of the slice depends on the type but it's known to be a slice of
Because then I could rewrite my current macro
write_le! as a function rather than a macro.
I don't want to write out
w.write_all(&(expr).to_le_bytes())?; hundreds of times, so I have a macro wrapping that:
The latest thing I looked at was
rand. I've put an assert on lengths before a hot loop so that the compiler would optimize away the bounds checks and
get_unchecked() would not be needed. This seems to be a very valuable but little-known trick. I think we should document it somewhere
Where did the value come from right before the loop, that it had a bound but the compiler didn't know that?
A surprisingly unsafe-heavy crate is
log. It tries to have an atomic integer that actually represents an enum, and conversions to and from that enum are necessarily unsafe and unchecked because they're on the hot path for logging, and they don't want to have unwinding code in there. I think it also hand-rolls a
no_std variant of
lazy_static inside the crate.
@Josh Triplett here's the PR: https://github.com/rust-random/rand/pull/960
Interesting. So, I get why dd is less than 512 (the compiler should really know that the result of
% 512 will be less than 512), but what makes
cc + 15 < 512? How does the code know that?
Oh, I see the assertion right above that, nevermind.
I wonder how hard it would be to get the compiler to do some numerical reasoning like that?
GCC is capable of some amazing reasoning about bit patterns in a value; you can hand-write things like byte swaps and rotates, and they'll turn into the corresponding instruction.
I probably should dust off https://github.com/rust-lang/rfcs/pull/2714, I have an implementation of the current proposed design handy but I'm not sure it's the best design anymore. It's the easiest one to explain, but it doesn't really solve the problem in the fastest possible way, so people might still want to hand-roll a custom implementation. And the fastest possible version is harder to explain, even though it's more general
@nikomatsakis I'll look through safety-dance crates and make an issue on a dedicated repo for every currently unavoidable
unsafe I find, then send you a link. That way we can have a clear set of problems and it would make sense to start brainstorming solutions. How's that for a plan?
Also I've been replacing a ton of ad-hoc unsafe with variants of
u16::from_le_bytes([..2].try_into().unwrap()) which is fine, but counter-intuitive. I'm told that const generics might help with the
@Shnatsel that sounds like a good plan
Regarding bounds checks: a quick search through crates.io shows that there are currently 10670 invocations of
_mut variant) across 864 crates, and the median number of uses per crate is 3.
Wow, that's kind of astounding.
How many crates use it?
oh heh it's... right there in the message
OK, so that is "3 uses per crate that use
get_unchecked, not 3 uses per crate on crates.io or something...
About safe abstractions. Someone made a nice (but a little outdated since it's in '18) list here: