Supporting `CtOption::map` for non-`Default` types?
gendx opened this issue · 5 comments
For now, CtOption::map
requires a Default
type.
This is especially problematic for types which require some constant-time validation of the input in their public constructors, say with the following API.
impl T {
pub fn from_bytes(bytes: &[u8; N]) -> CtOption<T> { ... }
}
In that case, exposing a Default
for T
would allow the caller to bypass from_bytes
validation by creating a Default
.
Could there be a variant of map
with an explicitly provided dummy value? Same question for CtOption::and_then
.
Hmm, regardless of the Default
bound, that code really looks like it should be using Option<T>
or Result<T,E>
, because if the input is invalid, the computation can't sensibly continue.
One thing that might work to relax the Default
bound would be to unconditionally apply the closure to the CtOption
's T
value, but I haven't thought it through yet...
If #94 were addressed which would unlock using combinators like CtOption::map
with heap-allocated types (which we need for the rsa
and dsa
crates), the use of Default
in map
would become a problem, because our T
values can vary in precision (i.e. number of limbs), and the Default
impl can't match precision to an existing value of T
except by chance.
Unconditionally applying the closure to T
would address this problem and prevent a spurious heap allocation (i.e. getting the Default
) every time map
is called.
It's also problematic to use Default
because it's typically a "zero value" which may not make sense given the mathematical operation being applied in the closure, e.g. it could be a divisor and division-by-zero could potentially cause a panic, precluding the use of map
in cases where it would work if the closure were unilaterally applied to the contained value instead of Default
.
I have proposed removing this bound in #118, although I worry it's a breaking change.
An non-breaking alternative would be to add new methods which do the same thing but don't have the bound.
I'm going to add my voice here that Default
is a bad trait to begin with; if we force types to have a Default
impl in order to make them interoperable with subtle
, this also means that the Default
impl is available in other places, which could cause the default value to inadvertently be constructed and incorrectly interpreted as valid data. This is a general type safety problem; Default
just shouldn't exist, and forcing users to implement it is detrimental to the overall safety of systems.