Question
I am using Rust and learning it from a JavaScript background. I want to know whether one struct can extend another struct so that the new struct automatically contains all the fields of the original struct.
For example, in JavaScript with ES6 classes, I could write:
class Person {
constructor(gender, age) {
this.gender = gender;
this.age = age;
}
}
class Child extends Person {
constructor(name, gender, age) {
super(gender, age);
this.name = name;
}
}
I would like to do something conceptually similar in Rust.
A constraint is that the original struct comes from an external Cargo package, so I cannot modify its definition.
I found older material about single inheritance and virtual structs, but that feature was removed. I also found suggestions about using traits, but since the original type is from another crate, I do not think I can convert it into a trait.
What is the correct Rust approach for this situation?
Short Answer
By the end of this page, you will understand why Rust structs do not support field inheritance like JavaScript or classical OOP languages, and what to use instead. You will learn the idiomatic Rust solution: composition—putting one struct inside another—plus how traits and wrapper types help you model shared behavior safely and clearly.
Concept
Rust does not support struct inheritance where one struct automatically gains all the fields of another struct.
If you have a type from another crate and want your own type to include it, the standard Rust solution is composition:
struct Child {
person: Person,
name: String,
}
This means Child contains a Person instead of being a specialized Person through inheritance.
Why Rust avoids field inheritance
Rust is designed around:
- clear ownership
- explicit data layout
- predictable behavior
- composition over inheritance
Inheritance can blur the structure of data and create tight coupling. Rust prefers simpler building blocks:
- structs for storing data
- impl blocks for methods
- traits for shared behavior
- composition for combining types
Important point about external types
Even if Person comes from another crate, you can still use it as a field inside your own struct. You do not need to modify Person to do that.
What you lose compared to inheritance
Mental Model
Think of Rust structs like real-world objects packed in boxes.
- In inheritance-based languages, a
Childbox might be treated as a largerPersonbox with extra compartments added on. - In Rust, a
Childbox usually contains a separatePersonbox inside it.
So instead of saying:
- “Child is a Person with more fields”
Rust often says:
- “Child has a Person and also has more fields”
This is called composition.
A simple analogy:
- Inheritance: building a taller version of the same cabinet
- Composition: placing one cabinet inside a larger storage unit with extra drawers
Rust strongly prefers the second approach because it is explicit and easier to reason about.
Syntax and Examples
The core Rust pattern is to store the existing struct as a field.
struct Person {
gender: String,
age: u32,
}
struct Child {
person: Person,
name: String,
}
You create values like this:
fn main() {
let person = Person {
gender: String::from("female"),
age: 8,
};
let child = Child {
person,
name: String::from("Mia"),
};
println!("{} is {} years old", child.name, child.person.age);
}
If the original struct comes from another crate
That is still fine. You can do this:
use external_crate::Person;
struct Child {
person: Person,
name: String,
}
Adding convenience methods
If you want Child to expose part of , add methods:
Step by Step Execution
Consider this example:
struct Person {
gender: String,
age: u32,
}
struct Child {
person: Person,
name: String,
}
fn main() {
let p = Person {
gender: String::from("female"),
age: 7,
};
let c = Child {
person: p,
name: String::from("Ava"),
};
println!("{} is {} years old", c.name, c.person.age);
}
Step by step
-
Personis defined with two fields:genderage
-
Childis defined with:person, which stores a fullPerson
Real World Use Cases
Composition is used constantly in real Rust programs.
API request context
You may wrap a library type with your app-specific metadata:
struct AuthenticatedRequest {
request: HttpRequest,
user_id: String,
}
Database models
You might store a shared record and add view-specific fields:
struct UserProfile {
user: User,
is_online: bool,
}
Configuration objects
A crate may provide a base config type, and your app adds environment-specific settings:
struct AppConfig {
base: ExternalConfig,
debug_mode: bool,
}
Parsing and validation
A parsed object from a library can be wrapped with validation results:
struct CheckedDocument {
document: ExternalDocument,
is_valid: bool,
}
UI or game state
A reusable type can be embedded inside a richer application struct:
{
person: Person,
score: ,
}
Real Codebase Usage
In real Rust projects, developers usually combine composition with a few common patterns.
1. Wrapper structs
A type from another crate is placed inside your own type.
struct Child {
person: Person,
name: String,
}
This is the most common solution.
2. Convenience methods
To avoid repeated nested access, developers add methods:
impl Child {
fn age(&self) -> u32 {
self.person.age
}
}
This creates a cleaner public API.
3. Validation at construction time
A constructor can check inputs before creating the struct:
impl Child {
fn new(person: Person, name: String) -> Self {
Self { person, name }
}
}
If validation is needed:
{
(person: Person, name: ) <, > {
name.().() {
(::());
}
( { person, name })
}
}
Common Mistakes
Mistake 1: Expecting field inheritance
Beginners often try to access inner fields directly as if they were inherited.
struct Child {
person: Person,
name: String,
}
// wrong idea
// let age = child.age;
Use:
let age = child.person.age;
Or add a method:
impl Child {
fn age(&self) -> u32 {
self.person.age
}
}
Mistake 2: Thinking traits copy fields
Traits define behavior, not stored data.
trait HasAge {
fn age(&self) -> u32;
}
A trait cannot automatically add an age field to a struct.
Mistake 3: Assuming you must modify the external crate
Comparisons
| Concept | What it does | Includes fields automatically? | Best use |
|---|---|---|---|
| Inheritance | One type extends another | Yes in OOP languages | Classical OOP hierarchies |
| Composition | One struct contains another | No | Idiomatic Rust data modeling |
| Trait | Shares behavior through methods | No | Common interfaces across types |
| Newtype | Wraps a single existing type | No | Add meaning, methods, or trait impls |
Composition vs inheritance
| Question | Inheritance-style answer | Rust-style answer |
|---|---|---|
Cheat Sheet
// Composition: preferred Rust approach
struct Child {
person: Person,
name: String,
}
Rules to remember
- Rust structs do not support field inheritance.
- Use composition to include one struct inside another.
- Use traits to share behavior, not fields.
- External crate types can still be used as fields in your own structs.
- Nested access looks like
child.person.age. - Add methods if you want a simpler API.
Common patterns
impl Child {
fn new(person: Person, name: String) -> Self {
Self { person, name }
}
fn age(&self) -> u32 {
self.person.age
}
}
If you only need behavior
trait HasAge {
fn age(&self) -> ;
}
FAQ
Can one struct inherit fields from another in Rust?
No. Rust does not support struct field inheritance. Use composition instead.
What is the Rust equivalent of extends for structs?
Usually there is no direct equivalent. The idiomatic replacement is to store one struct inside another.
Can I extend a struct from an external crate?
You cannot add fields to the original struct, but you can wrap it inside your own struct and add your own fields there.
Should I use traits instead of inheritance in Rust?
Use traits when you want shared behavior. Use composition when you want to store shared data.
Why does Rust prefer composition over inheritance?
Composition is explicit, flexible, and works well with Rust's ownership and safety model.
Can I make nested fields easier to access?
Yes. Add methods on your outer struct that return or expose the inner data.
Is duplicating all fields from the original struct a good idea?
Usually no. It creates maintenance problems and can drift from the original type definition.
What if I want my type to behave like the inner type?
Wrap the inner type and implement methods or traits that delegate to it.
Mini Project
Description
Build a small Rust program that models a student profile by wrapping an existing Person struct inside a Student struct. This demonstrates the Rust way to reuse data from one type while adding your own fields and methods.
Goal
Create a Student type that contains a Person, adds school-specific data, and exposes convenient methods without using inheritance.
Requirements
Create a Person struct with age and gender fields.
Create a Student struct that contains a Person and adds a name and grade field.
Implement a constructor method for Student.
Add methods that return the student's age and name.
Print a formatted summary of a student in main.
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 Option<String> to Option<&str> in Rust
Learn the idiomatic Rust way to convert Option<String> into Option<&str> using as_deref, as_ref, and unwrap_or without extra allocation.