ProgrammingRust Developer

How do constructors and associated functions for structs work in Rust, and what is the difference between new and with_capacity? How should they be implemented correctly?

Pass interviews with Hintsage AI assistant

Answer.

Background

In Rust, there are no classic constructors like in OOP languages, but there are associated functions (most frequently new) that implement the constructor pattern with explicit initialization. Associated functions allow creating struct instances with preset values or creating them with additional logic (for example, by reserving memory).

Problem

Many start using only new, forgetting about other factory method patterns (for example, with_capacity), or incorrectly implement struct initialization (especially with private/public fields), which can lead to invariant errors.

Solution

An associated function of type new is implemented through impl, and it can be the only entry point for creating objects with invariants. Functions like with_capacity provide additional capabilities, for example, to pre-allocate memory.

Example code:

pub struct MyBuffer { data: Vec<u8>, } impl MyBuffer { pub fn new() -> Self { MyBuffer { data: Vec::new() } } pub fn with_capacity(cap: usize) -> Self { MyBuffer { data: Vec::with_capacity(cap) } } }

Key features:

  • Associated functions do not receive self, they work like static methods
  • Public factory methods help establish invariants
  • You can implement different "constructors" for various scenarios

Trick questions.

If new is not implemented, will Rust create it by default?

No. There is no auto-generation of new. If the method is not explicitly implemented, it will not exist, even if the struct has only public fields.

What is the fundamental difference between new and Default?

Default is a trait constructor that can only be called where the type has implemented or explicitly inherited Default. It is standard for generic algorithms but does not always coincide with new.

Can new and with_capacity methods be used for private structs?

Yes, methods can be made public or private, but creating a public constructor for a private struct does not allow users to create instances of that struct outside the module.

Common mistakes and anti-patterns

  • Making all struct fields public, allowing for the creation of incorrect values outside the constructor
  • Not implementing with_capacity for collections where memory allocation savings are significant
  • Implementing new that does not correctly initialize all fields

Real-life example

Negative case

An open struct without invariants, all fields are public:

pub struct User { pub login: String, pub active: bool }

Pros:

  • Easy to create objects

Cons:

  • Possible creation of incorrect values (for example, empty login, uninitialized fields)

Positive case

Private fields, creation only through new, invariants ensured:

pub struct User { login: String, active: bool } impl User { pub fn new(login: String) -> User { User { login, active: true } } }

Pros:

  • Protection against incorrect data
  • Control over validation and initialization logic

Cons:

  • Need to write additional methods – a bit more code