6 ms·
> Even if it were stable, it only works with slices of primitive types, so we’d have to lose our newtypes (SymbolId etc). That's weird. I'd expect it to work
by quotemstr 1y ago
> Even if it were stable, it only works with slices of primitive types, so we’d have to lose our newtypes (SymbolId etc).
That's weird. I'd expect it to work with _any_ type, primitive or not, newtype or not, with a sufficiently simple memory layout, the rough equivalent of what C++ calls a "standard-layout type" or (formerly) a "POD".
I don't like magical stdlibs and I don't like user types being less powerful than built-in ones.
Clever workaround doing a no-op transformation of the whole vector though! Very nearly zero-cost.
> It would be possible to ensure that the proper Vec was restored for use-cases where that was important, however it would add extra complexity and might be enough to convince me that it’d be better to just use transmute.
Great example of Rust being built such that you have to deal with error returns and think about C++-style exception safety.
> The optimisation in the Rust standard library that allows reuse of the heap allocation will only actually work if the size and alignment of T and U are the same
Shouldn't it work when T and U are the same size and T has stricter alignment requirements than U but not exactly the same alignment? In this situation, any U would be properly aligned because T is even more aligned.
- aw1621107 1y ago> I'd expect it to work with _any_ type, primitive or not, newtype or not, with a sufficiently simple memory layout, the rough equivalent of what C++ calls a "standard-layout type" or (formerly) a "POD". This might be related in part to the fact that Rust chose to create specific AtomicU8/AtomicU16/etc. types instead of going for Atomic<T> like in C++. The reasoning for forgoing the latter is [0]: > However the consensus was that having unsupported atomic types either fail at monomorphization time or fall back to lock-based implementations was undesirable. That doesn't mean that one couldn't hypothetically try to write from_mut_slice<T> where T is a transparent newtype over one of the supported atomics, but I'm not sure whether that function signature is expressible at the moment. Maybe if/when safe transmutes land, since from_mut_slice is basically just doing a transmute? > Shouldn't it work when T and U are the same size and T has stricter alignment requirements than U but not exactly the same alignment? In this situation, any U would be properly aligned because T is even more aligned. I think this optimization does what you say? A quick skim of the source code [1] seems to show that the alignments don't have to exactly match: //! # Layout constraints //! <snip> //! Alignments of `T` must be the same or larger than `U`. Since alignments are always a power //! of two _larger_ implies _is a multiple of_. And later: const fn in_place_collectible<DEST, SRC>( step_merge: Option<NonZeroUsize>, step_expand: Option<NonZeroUsize>, ) -> bool { if const { SRC::IS_ZST || DEST::IS_ZST || mem::align_of::<SRC>() < mem::align_of::<DEST>() } { return false; } // Other code that deals with non-alignment conditions } [0]: https://github.com/Amanieu/rfcs/blob/more_atomic_types/text/0000-integer_atomics.md https://github.com/Amanieu/rfcs/blob/more_atomic_types/text/... [1]: https://github.com/rust-lang/rust/blob/c58a5da7d48ff3887afe4c618dc04defdee3dab5/library/alloc/src/vec/in_place_collect.rs https://github.com/rust-lang/rust/blob/c58a5da7d48ff3887afe4...
- quotemstr 1y ago> I think this optimization does what you say? Cool. Thanks for checking! I guess the article should be tweaked a bit --- it states that the alignment has to match exactly.
- 0x1ceb00da 1y ago> Great example of Rust being built such that you have to deal with error returns and think about C++-style exception safety. Not really. Panics are supposed to be used in super exceptional situations, where the only course of action is to abort the whole unit of work you're doing and throw away all the resources. However you do have to be careful in critical code because things like integer overflow can also raise a panic.
- quotemstr 1y ago> be careful in critical code because things like integer overflow can also raise a panic So you can basically panic anywhere. I understand people have looked at no-panic markers (like C++ noexcept) but the proposals haven't gone anywhere. Consequently, you need to maintain the basic exception safety guarantee [1] at all times. In safe Rust, the compiler enforces this level of safety in most cases on its own, but there are situations in which you can temporarily violate program invariants and panic before being able to restore them. (A classic example is debiting from one bank account before crediting to another. If you panic in the middle, the money is lost.) If you want that bank code to be robust against panics, you need to use something like https://docs.rs/scopeguard/latest/scopeguard/ https://docs.rs/scopeguard/latest/scopeguard/ In unsafe Rust, you basically have the same burden of exception safety that C++ creates, except your job as an unsafe Rust programmer is harder than a C++ programmer's because Rust doesn't have a noexcept. Without noexcept, it's hard to reason about which calls can panic and which can't, so it's hard to make bulletproof cleanup paths. Most Rust programmers don't think much about panics, so I assume most Rust programs are full of latent bugs of this sort. That's why I usually recommend panic=abort. [1] https://en.wikipedia.org/wiki/Exception_safety#Classification https://en.wikipedia.org/wiki/Exception_safety#Classificatio...
- 0x1ceb00da 1y ago> If you panic in the middle, the money is lost. Wouldn't a relational database help deal with this? > Without noexcept, it's hard to reason about which calls can panic and which can't, so it's hard to make bulletproof cleanup paths. Unsafe blocks usually contain very low level code, so you can understand what your code does very accurately. If the unsafe code calls a dependency which calls 150 other dependencies transitively, yeah, that's going to be pretty bad.