Description
We need type level numerics before Default ::default()
, or any trait based approach, can work for large arrays, because users cannot impl Default for [u8; 256]
since neither Default
nor [u8; 256]
were defined in their crate.
We cannot create a macro that builds everything from clear_on_drop.rs
and core/default.rs
onto a user version of Default::default()
because then nobody can call across crate boundaries. In fact, users can still wrap their types so that they can impl Default
for the wrapper, but that results in boilerplate code like this :
fn kdf(..) {
struct X([u8; 256]);
impl Default for X { fn default() -> Self { X([0u8;256]) } }
let x: X = Default::default();
...
}
You need T: Copy
anyways because otherwise ClearOnDrop<T>
works with T: Drop
types. I initially tried to support a limited subset of Drop
types in https://github.com/burdges/zerodrop-rs/blob/master/src/zdd.rs#L42 but @bluss pointed out irreparable problems burdges/zerodrop-rs#7 in fact, we do not know that *_place = Default::default()
does not read *_place
out onto the stack to call the destructor if T: Drop
.
I believe we can justify zeroing a T: Copy
type with a simple memset
like operation. We might violate a core::nonzero::NonZero
bound applied to a non-pointer type, which afaik do not yet exist. That's okay if P
is Box<T>
, etc. since T
is going away and Copy
types cannot be Drop
. About the only way to violate that assumption is with something like
fn kdf(..) {
let _x: SomeNonZeroType;
let x = CopyOnDrop(&mut x);
let x: X = Default::default();
...
::std::mem::drop(x);
assert_eq!(Some(_x), None);
}
I think the question now becomes: Which memset? You explained the problems with core::intrinsics::volatile_set_memory
. We could call libc::memset
or write a new one. Anything I'm missing?