[Idioms/Constructors] use Default instead of empty constructor #276
Replies: 2 comments 2 replies
-
Forgot to mention this: having both (I don't know if this principle has a name, like KISS or DRY.) |
Beta Was this translation helpful? Give feedback.
-
I want to cite from Elegant Library APIs in Rust:
Also Rust API Guidelines:
@wookietreiber Do you want to make a pull request with the changes you propose? |
Beta Was this translation helpful? Give feedback.
-
TL;DR: Use
Default
instead offn new()
.The constructors section currently shows an example with
fn new()
, i.e. a constructor without arguments. For data structures like this, it is often useful to provide aDefault
implementation.Default
is used in a few places in libraries, e.g. instd
inOption::unwrap_or_default
,Result::unwrap_or_default
, orEntry::or_default
, so it's definitely useful to have.When there is a
Default
implementation, and for data structures with empty constructors there arguably should be one, providingfn new()
is unnecessary, even counterproductive.My suggestion is to explain exactly this in the constructors section, maybe even using the variants provided below: going from a less capable version (no default), to having a more capable version (with default), to having even less code than in the initial example (derived default).
Note: I know that there is also a section about
Default
itself. Since I'm advocating forDefault
instead offn new()
, you might even want to swap these sections, so the section aboutDefault
gets read before the one about constructors. I can also imagine to merge these sections.Variant A: only new without arguments
Note: This variant doesn't have a
Default
implementation.Variant B: new + manual Default
Note: This example shows duplicated code. But one could also implement
new
usingSelf::default()
or implementDefault
usingSelf::new()
. Which one should I choose? Avoid this unnecessary decision by not implementing both. One might even make the mistake of having different implementations fornew
andDefault
. #dangerzoneVariant C: new + derived Default
Note: Here, I opted to use
Self::default()
innew
. But I could also have implemented it asSelf { o: None }
. This would be code duplication as well, you just don't see it because of#[derive(Default)]
.Variant D: only Default
Note: You can't get by with writing less code, which reduces complexity and means less code maintenance.
Drawback
For programmers coming from other languages,
Default
may be a new concept. They might look fornew
constructors instead of checking if there is aDefault
implementation and wonder where the constructor is. This could be handled if rustdoc would more prominently show that there is aDefault
implementation. Currently, you usually have to scroll way down to see if there is one.Personal Summary
I find having
Default
in Rust is awesome. I useDefault
for all of my data structures that are able to be constructed without arguments. Once I realized that it is functionally equivalent to havingfn new()
in terms of having a constructor without arguments, and even more functional by being able to be used in places where libraries requireDefault
, I have stopped implementingfn new()
, and you can too! 😄Beta Was this translation helpful? Give feedback.
All reactions