Proposal: Add is_default() automatically to types that implement Default and PartialEq

I’m not a fan of the Default trait, because it has next to no semantics. It is nowhere defined what the ‘default’ value is supposed to actually represent. Let’s look through the list of implementations:

  • For number types (integers and floats) it’s zero.
  • For char, it’s '\0', i.e. U+0000; char does not have defined addition or multiplication, so this isn’t ‘zero’ in the same sense as above.
  • For Option<_>, it’s None. (I can imagine Some(0) to be more useful starting value in some situations, e.g. if you’re using the None variant as an improptu NaN-like value.)
  • For str and slices, it’s the empty slice, and likewise for their growable equivalents: Vec and String.
  • More generally, for containers that can hold any number of items, it’s an appropriate empty container.
  • For Mutex<_>, RwLock<_>, ManuallyDrop<_>, it’s the default value of the underlying type, if it exists.
  • For Rc/Arc, it’s a singly-referenced default of the underlying type, but for Weak, it’s a ‘stillborn’ weak pointer with no backing storage.
  • For Cow, it’s a default owned value, even if the borrowed variant implements Default as well (which I imagine would be more lightweight.)

In a generic setting, things are more murky: the ‘default’ value cannot be said to have any particular properties. Is it the smallest possible value? (True for char and unsigned integers; sorta-true for containers; false for signed integers and floating-point.) Is it the identity element of +? (True for number types, Vec and String.) Does it represent ‘a lack of a value’, whatever it may mean? (True for containers, Weak and Option; false for floating-point types, since they the default isn’t a NaN.)

These examples have rather little to do with each other, other than some vague handwave-y notion of ‘zero’ or ‘emptiness’ (and delegating to the wrapped type). It may be what you need most of the time, but that’s primarily because you already know what the concrete type is in a given situation and what you need it for. With its semantics so nebulous, Default seems useful as little more than a typing aid.

These questions will be even more pertinent for the proposed is_default method: when you know that a given value is the ‘default’, what can you really say about it? As I point out, it’s not all that much; but I fear some people are going to assume more things about it than is actually guaranteed anyway (I have already seen someone use Default::default() as a generic zero), which means the proposed functionality is at risk of becoming a correctness hazard.

4 Likes