Question
In Rust, I have a struct where each field needs an initial default value when the struct is created. Right now I am using a new() function that manually assigns every field:
struct CParams {
i_insert_max: i64,
i_update_max: i64,
i_delete_max: i64,
i_instance_max: i64,
t_first_instance: bool,
t_create_tables: bool,
t_continue: bool,
}
impl CParams {
fn new() -> CParams {
CParams {
i_insert_max: -1,
i_update_max: -1,
i_delete_max: -1,
i_instance_max: -1,
t_first_instance: false,
t_create_tables: false,
t_continue: false,
}
}
}
I would prefer a shorter or more idiomatic way to give struct fields default values when the struct is instantiated. Is there a more concise approach in Rust?
Short Answer
By the end of this page, you will understand how Rust initializes struct fields, why fields cannot hold inline default values in the struct definition itself, and how to use idiomatic alternatives such as a new() constructor, the Default trait, and struct update syntax to keep initialization concise and maintainable.
Concept
In Rust, a struct definition describes the shape of the data, not the values each field should start with. That means you cannot write default field values directly inside the struct declaration like some other languages allow.
Instead, Rust uses a few common patterns:
- A constructor function such as
new() - The
Defaulttrait for standard default values - Struct update syntax to override only a few fields
Why this matters:
- It keeps type definitions separate from object creation logic.
- It makes initialization explicit.
- It scales better when your struct grows or when multiple initialization styles are needed.
For your example, manually writing all fields inside new() works, but the more idiomatic Rust approach is often to implement Default. Then you can create values with:
let params = CParams::default();
If you want mostly default values but need to change one or two fields, you can combine that with struct update syntax:
let params = CParams {
i_insert_max: 10,
..Default::default()
};
This is both shorter and easier to maintain.
Mental Model
Think of a Rust struct as a blueprint for a form.
The struct says:
- these are the fields that exist
- these are their types
But the struct does not say what values should be filled in automatically.
To actually fill out the form, you need a helper:
new()is like filling the form by hand each timeDefaultis like having a pre-filled template- struct update syntax is like taking the template and changing only a few boxes
So the struct is the form layout, and Default is the saved template.
Syntax and Examples
Basic constructor
A straightforward solution is to keep a new() method:
struct CParams {
i_insert_max: i64,
i_update_max: i64,
i_delete_max: i64,
i_instance_max: i64,
t_first_instance: bool,
t_create_tables: bool,
t_continue: bool,
}
impl CParams {
fn new() -> Self {
Self {
i_insert_max: -1,
i_update_max: -1,
i_delete_max: -1,
i_instance_max: -1,
t_first_instance: false,
t_create_tables: false,
t_continue: false,
}
}
}
This is valid and clear. Using Self is slightly shorter than repeating CParams.
Using the Default trait
A more idiomatic solution is to implement Default:
Step by Step Execution
Consider this example:
struct CParams {
i_insert_max: i64,
i_update_max: i64,
t_continue: bool,
}
impl Default for CParams {
fn default() -> Self {
Self {
i_insert_max: -1,
i_update_max: -1,
t_continue: false,
}
}
}
fn main() {
let params = CParams {
i_insert_max: 50,
..Default::default()
};
println!("{}", params.i_insert_max);
println!("{}", params.i_update_max);
println!("{}", params.t_continue);
}
What happens step by step
- Rust sees the
CParamsstruct definition. - Rust sees the
Defaultimplementation forCParams. - In , a new value is created.
Real World Use Cases
Default struct values are common in real Rust programs.
Configuration objects
A program may have settings like:
- timeout values
- retry limits
- feature flags
You can define sensible defaults and override only what is needed.
API request options
An HTTP client might use a struct like:
follow_redirects: truetimeout_secs: 30use_cache: false
Most callers use defaults. A few callers customize specific fields.
Command-line tools
A CLI app may store parsed options in a struct. If the user does not provide a flag, the program uses defaults.
Test data setup
Tests often need objects that are mostly the same. A Default implementation lets you create a base object and change only the fields relevant to a test.
Stateful application logic
Structs representing application state often need a known initial state before processing begins.
Real Codebase Usage
In real projects, developers commonly use Default in a few standard patterns.
Pattern: default configuration object
let config = AppConfig::default();
This gives a clean starting point.
Pattern: override only what matters
let config = AppConfig {
debug: true,
..Default::default()
};
This is very common in tests and setup code.
Pattern: combine new() with Default
Sometimes a type provides both:
impl MyType {
fn new() -> Self {
Self::default()
}
}
This is useful when users naturally expect a new() constructor.
Pattern: validation after construction
Common Mistakes
Mistake 1: Expecting inline field defaults in the struct definition
This is not valid Rust:
struct CParams {
i_insert_max: i64 = -1,
t_continue: bool = false,
}
Rust structs declare field names and types only.
Mistake 2: Using derive(Default) when custom values are needed
#[derive(Default)]
struct CParams {
i_insert_max: i64,
t_continue: bool,
}
This gives:
i_insert_max = 0t_continue = false
If you need -1, derive(Default) is not enough. Write impl Default manually.
Mistake 3: Forgetting required fields without ..Default::default()
Broken code:
= CParams {
i_insert_max: ,
};
Comparisons
| Approach | When to use it | Pros | Cons |
|---|---|---|---|
new() method | You want a familiar constructor | Clear and explicit | Can become repetitive |
Manual Default implementation | You need custom defaults like -1 | Idiomatic, reusable, works well with struct update syntax | Slightly more code up front |
#[derive(Default)] | Type defaults are correct for every field | Very short | Cannot set custom defaults like -1 |
| Struct update syntax | You want to override a few fields | Concise and readable | Usually depends on Default or another existing struct |
Cheat Sheet
Quick reference
Define a struct
struct CParams {
i_insert_max: i64,
t_continue: bool,
}
Constructor with new()
impl CParams {
fn new() -> Self {
Self {
i_insert_max: -1,
t_continue: false,
}
}
}
Implement Default
impl Default for CParams {
fn default() -> Self {
Self {
i_insert_max: -1,
t_continue: false,
}
}
}
Create with defaults
let = CParams::();
FAQ
Can I assign default values directly in a Rust struct definition?
No. Rust struct definitions only declare field names and types. Default values must be provided through construction logic such as new() or Default.
Is Default more idiomatic than new() in Rust?
Often, yes. If a type has a natural default state, implementing Default is idiomatic and works nicely with other Rust features.
When should I use #[derive(Default)]?
Use it when every field should use its type's built-in default value, such as 0 for integers and false for booleans.
Why doesn't #[derive(Default)] work for -1?
Because deriving Default uses the default of the field's type. For i64, that default is 0, not -1.
Can I keep both new() and Default?
Mini Project
Description
Create a small Rust configuration type for a command-line import tool. The struct should have sensible default values, but allow a caller to override only the settings they care about. This demonstrates the practical use of Default and struct update syntax in a realistic setup scenario.
Goal
Build a Rust struct with custom default values and create multiple configurations by overriding selected fields.
Requirements
- Define a struct named
ImportConfigwith at least four fields. - Implement the
Defaulttrait manually because at least one field must use a non-standard default value. - Create one config using
ImportConfig::default(). - Create another config that overrides one or two fields using struct update syntax.
- Print both configurations to verify the values.
Keep learning
Related questions
Accessing Cargo Package Metadata in Rust
Learn how to read Cargo package metadata like version, name, and authors in Rust using compile-time environment macros.
Associated Types vs Generic Type Parameters in Rust: When to Use Each
Learn when to use associated types vs generic parameters in Rust traits, with clear rules, examples, and practical API design advice.
Convert an Integer to a String in Rust
Learn the current Rust way to convert integers to strings, why `to_str()` no longer works, and when to use `to_string()` or `format!`.