`new` without `Default`?

I have a type that implements fn new() -> Self in a non-trivial way (it allocates memory etc).

Clippy says there should be a corresponding Default implementation. I disagree -- I feel that Default should only really be implemented for trivial constructors that don't do any work beyond filling in the struct with some default bits.

Is Clippy wrong here?

I csnt day what the official view is insofar there is one; But personally I view Default as appropriate if what results from a call to Type::default() can indeed be considered a default value. I don't really consider allocations in that.

However, if instead I saw Type::new() it wouldn't bug me or anything.

2 Likes

I don't think default has anything to do with allocations. BTreeMap::new() used to allocate for a long time, until it was deemed necessary to shift the set of trade-offs in such a way that it can become const and allocation-free.

3 Likes

I consider Clippy overzealous on this one.

4 Likes

The main reason to consider implementing Default, like any trait, is for the sake of generic code and derives. So, ask yourself what makes sense to happen when someone writes #[derive(Default)] on a struct containing your type, or calls mem::take() on a reference to your type — should these reasonably succeed with a particular value, or should they be a compilation error?

As I see it, Default should usually be implemented for any type that has a meaningful “default”, “empty”, “zero” value; a single allocation doesn't disqualify it. (Box<T> implements Default, after all, and surely that makes sense.)

Some reasons that do seem sufficient to not implement Default, while still having a zero-argument new:

  • All instances are meaningfully unequal to each other — like a unique ID type where new() != new(). In this case, you want callers to think carefully about when they create a new one vs. using an existing one.
  • There is an empty value, but letting it be used by default would likely be a hazard, like “this log-sink discards all inputs”.
  • new() has a noteworthy side effect, like connecting to a server. (But are you sure that’s a good idea at all?)
  • “Creation is very expensive” does seem like a good reason, but what “very expensive” is really depends on the application.
16 Likes

I would consider whether there's a name other than new you could give it, which IIRC would also shut up clippy.

If it's doing something non-trivial, can you describe what it's doing in the name? Not a perfect parallel, but it makes me think of how it's not File::new, but File::open because the non-trivialness means there's a better word than the "meh whatever" choice of new.

But if there's nothing interesting to say and it's just that it's doing more than sticking in some integer literals, I don't think that's a reason to avoid implementing Default.

5 Likes

One non-mentioned case where I would use new() without Default is if there is any possibility that the zero-argument non-parametric constructor will be impossible in the future. It is much easier to change method signatures than to root out an unapplicable trait impl. Unless I'm certain that I'll never have to add extra arguments or type parameters to the constructor, I'd rather stay away from Default. Same logic applies to any possible From conversion: if I'm not certain that such conversion will always exist, it's easier to go with special-case methods.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.