4 ms·
I don't really feel like this is an issue of "zero cost abstractions". The baseline expectation should be that it will do something reasonable, which in this ca
by stormbrew 5y ago
I don't really feel like this is an issue of "zero cost abstractions". The baseline expectation should be that it will do something reasonable, which in this case is probably `if Copy do bit-setting with memset/bzero if possible, otherwise clone() loop` which is what it appears to do according to a reddit comment linked to elsewhere.
I'm not really sure it follows, though, that you should ever expect more than that. It's nice if you can get it but the presence of a very specific optimization shouldn't be taken as the new "zero cost" baseline, and I don't think it should be taken as given that a newtype will inherit all possible optimizations of its enclosed type.
- erk__ 5y agoI think a proper solution would be to expose the trait `IsZero` [0] as that is used to figure out when it is correct to use `calloc` instead of having to do `malloc` -> `memset` [1]. I don't know if there is any plans for exposing that trait, but it would be a nice thing to see. [0]: https://github.com/rust-lang/rust/blob/master/library/alloc/src/vec/is_zero.rs https://github.com/rust-lang/rust/blob/master/library/alloc/... [1]: https://github.com/rust-lang/rust/blob/master/library/alloc/src/vec/spec_from_elem.rs#L50..L60 https://github.com/rust-lang/rust/blob/master/library/alloc/...
- conradludgate 5y agoI think you don't need an IsZero trait. It needs to know if the bytes are zero. A custom implementation of IsZero might not hold that guarantee. However, if the type is Copy and all the bytes are zeros, then you know it can be calloc
- Measter 5y agoYou mean by checking the bytes of the data directly kinda like in this godbolt example: https://godbolt.org/z/78cvfshva https://godbolt.org/z/78cvfshva
- tialaramex 5y ago(As you likely know) Normally in Rust the trait is a gentleman's agreement. If my type does not have total Equivalence then I don't implement the Eq trait, and so if a type has Eq then it's promising to deliver total Equivalence. You can, in fact, just defy this and make types that do something "completely wrong" while implementing the syntax of the Trait and I provide a suite of them so that other people don't need to do that experiment: https://crates.io/crates/misfortunate https://crates.io/crates/misfortunate However, Rust does have two other tricks up its sleeve: 1. Unsafe traits. You can declare a trait to be "unsafe". Implementations of this trait likewise need that "unsafe" keyword and it alerts the programmer that, as with unsafe function calls, they are responsible for actually getting the implementation details correct. Not many standard library traits are unsafe because that's a considerable burden, but a few are, including Send and Sync. You wouldn't learn much by implementing unsafe traits wrongly in a library like misfortunate, it's the same lesson as a C++ library that scribbles on random memory addresses. 2. Rust knows whether you implemented a trait by hand or merely derived it. In a few cases it can be reasonable for the language to distinguish these cases for built-in traits, since in the former case it knows the derived trait does what is needed even though a manual implementation may not. It is possible that future improvements to Const Generics will rely on this latter idea. So long as you derive Eq rather than implementing it, Rust can reason that your type really is suitable as a constant type parameter whereas misfortunate::Always is not suitable (it would cause mayhem if permitted because of its idea of what "equality" is) despite claiming to be Eq.
- conradludgate 5y agoGood idea with unsafe traits. Maybe something like unsafe trait IsZero: Copy { fn is_zero(&self) -> bool; } #[derive(Copy, IsZero)] struct Foo { a: u8, b: u16, } which could derive into unsafe impl IsZero for Foo { fn is_zero(&self) -> bool { true && self.a.is_zero() && self.b.is_zero() } }
- conradludgate 5y agoThere needs to be some thought into how it works with Enums too. Rust currently has no built in standard for enum defaults (you have to implement default yourself). It would be nice to somehow mark one of the variants to be represented with the 0 discriminant (Like how Option::None is implemented)
- the8472 5y ago> However, if the type is Copy and all the bytes are zeros, then you know it can be calloc That's not as easy to check as one would think since Copy types can still contain uninitialized bytes.
- PoignardAzur 5y agoYeah, but in the common case all the uninitialized bytes are in the padding at the end of the struct (since Rust can re-arrange fields). Especially in a case like in the article, where the type is very small (one byte), and the obstacle is that the author is using a newtype.
- the8472 5y agoKnowing that they usually are at the end doesn't help since you still need to write a general runtime check that expresses "are all the bits for this type zero or uninit". Afaik currently there is no way to do this without causing UB. This would need some new intrinsic.
- dspillett 5y ago> The baseline expectation should be that it will do something reasonable Surely baseline is that it does something correct. Reasonable (or just "not pathologically stupid" if you separate the two, with reasonable next) comes after. To my mind an optimising compiler is a compiler first, an optimiser second.