-
-
Notifications
You must be signed in to change notification settings - Fork 80
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
Allow deriving Zeroable
with explicitly provided bounds
#190
Comments
uh, possibly? I'm unclear how the derive code accepting a custom bound could be sure it's the correct bound. Also, what's the ZeroableOption thing? |
The idea is that whatever is provided by the user will be used as the bound. If it is incorrect, the compiler will throw an error, so you, as an author of such macros, do not have to worry if the bound is correct. For example, consider the #[derive(Derivative)]
#[derivative(Clone(bound = ""))]
struct Foo<T> {
t: T
} The compiler will tell me that there is missing bound Regarding your second question, #[repr(u8)]
pub enum ZeroableOption<T> {
#[default]
None = 0,
Some(T),
} |
Well the compiler definitely will let you impl Zeroable when you have non-Zeroable fields. It's a unsafe trait with no methods, it could (in the "it will compile" sense) be implemented on anything. However, that's why it's unsafe, because the bounds of the impl must be correct for the type in question. Given your specific ZeroableOption type, i think perhaps you should just write down the unsafe impl directly. |
I think we have a small misunderstanding here :D
EDIT: Additional info here. In fact, until it is properly implemented in this carte, I've created an ugly small macro rules for it: #[macro_export]
macro_rules! derive_zeroable {
(
$(#$meta:tt)*
pub struct $name:ident $([ $($bounds:tt)* ] [ $($bounds_def:tt)* ])? {
$($field:ident : $ty:ty),* $(,)?
}
) => {
$(#$meta)*
pub struct $name $(< $($bounds_def)* >)? {
$($field : $ty),*
}
unsafe impl $(< $($bounds_def)* >)? $crate::Zeroable for $name $(< $($bounds)* >)?
where $($ty: Zeroable),* {}
};
} Which I'm using as: derive_zeroable! {
#[derive(Debug, Derivative)]
#[derivative(Default(bound = "T: Default"))]
pub struct LinkedArrayRefCell[T, N][T, const N: usize] {
size: Cell<usize>,
first_segment: OptRefCell<ZeroableOption<Segment<T, N>>>,
}
} and it generates the following Zeroable impl: unsafe impl<T, const N: usize> ::enso_frp2::Zeroable for LinkedArrayRefCell<T, N>
where Cell<usize>: Zeroable, OptRefCell<ZeroableOption<Segment<T, N>>>: Zeroable {} Please note, that if you generate the impl this way from the macro, there is no possibility for the user-provided bound to make the implementation unsafe, as it will always be checked by the compiler. |
I guess if the bound can't actually cause unsound impls to be emitted then it's fine to have a user provided bound. I'm not going to adjust the proc-macro myself, but if someone else does the PR we can get this added. |
I have a draft PR #196 that adds user-custom bounds for Zeroable, but considering that "Perfect Derive" is also possible (emit a bound for each field's type exactly), it might be better to just have code exampleIf the following derive#[derive(Zeroable)]
struct Struct<T, U, V> {
a: Cell<usize>,
b: PhantomData<T>,
c: Option<Box<U>>,
d: [V; 3],
} emitted the following impl unsafe impl<T, U, V> Zeroable for Struct<T, U, V>
where
Cell<usize>: Zeroable,
PhantomData<T>: Zeroable,
Option<Box<U>>: Zeroable,
[V; 3]: Zeroable,
{} that would work and not require the user to give any bounds. |
@zachs18 amazing to see it! Please note that if you are not emitting bounds automatically for every field, then this can't be considered Safe, as I could provide If you don't want the checks and want to have unsafe impl, you can always write |
See #196 (comment) for why the PR is indeed safe (it doesn't compile if the given bounds do not guarantee that all fields are Aside from that, I see a few main ways to resolve this issue:
IMO, given option 2, I don't see much use for the additional customization that 3 or 4 provide (perhaps so users can avoid the "semver hazard" by adding more conservative bounds than strictly necessary? So that a user could change their underlying type in their library without needing to strengthen a bound which might require a version bump). Note on "Perfect Derive": "Perfect Derive"-by-default has the "semver hazard" that changing private fields can change the public impl's bounds, similar to (IMO Option 2 is best, followed by 4, then 3, then 1) @Lokathor Which option do you think is best (or something else)? @wdanilo Would option 2 work for you, or would you rather have explicitly specified bounds? |
@zachs18 oh, I apologize for super late reply! Somehow I missed the notification. You can't just use "perfect derive" without allowing users to provide custom bounds, as this would be extremely limited. In order to keep old code working as is, we can indeed do something like that:
I believe that this will just cover all cases and will be a very consistent way of doing this (e.g. How does it sound to you? |
I also let this topic totally slip my mind. Reading the options, I agree that Option 2/(1b) seems best. If I understand right: It doesn't change any existing users, it just adds a new opt-in for people to use if the existing "basic derive" doesn't work in their case. EDIT: I admit that I don't remember enough to understand your concerns offhand
|
@Lokathor I think thy my point 2 is superior than 2/(1b) as it is more consistent with how other crates work. It provides the same features and matches the priorities you listed. @zachs18 do you agree with that? Basically, you don't need to write |
Hi, I've got a code like this:
And deriving
Zeroable
addsT: Zeroable
bound. This is bad, as I've gotimpl<T> Zeroable for ZeroableOption<T> {}
for anyT
implemented. Doing manualimpl<T, N> Zeroable for LinkedCellArray<T, const N> {}
is also bad, as it does not check the soundness when the code is changed. Would it be possible to add an option toZeroable
to provide a custom bound, something like the following? :)The text was updated successfully, but these errors were encountered: