What a field is
An empty Dog struct is an empty shell: every instance is identical and carries no information. A field, which other languages call a member variable, is a variable that belongs to a struct. Each instance gets its own copy of every field, so two dogs can have different names without interfering with each other.
Declaring fields
Fields go between the braces of the struct, each written as name: Type and separated by commas:
struct Dog {
name: String,
age: i32,
}
This struct has two fields. name is a String, an owned, growable piece of text, and age is an i32. A comma after the last field is allowed and is the usual style.
Reading and writing fields
A field is reached with the dot operator. The same syntax reads it and, on the left of =, writes it:
let mut dog = Dog { name: String::new(), age: 0 };
dog.name = "Rex".to_string(); // write
dog.age = 4;
println!("{}", dog.name); // read: Rex
Creating the instance requires a value for every field. Here the placeholders are an empty string and 0, and the next two lines overwrite them. Rust has no automatic default value for a missing field: leaving one out is a compile error ("missing field").
Each instance keeps its own data:
let mut a = Dog { name: "A".to_string(), age: 1 };
let b = Dog { name: "B".to_string(), age: 2 };
a.age += 10;
a.name.push_str("bc");
println!("{} {} {} {}", a.name, a.age, b.name, b.age); // Abc 11 B 2
Changing a left b alone. The expression a.name.push_str("bc") shows that a field can itself be an object with methods: a.name is a String, and push_str works on it.
The lesson's exercise, step by step
- Declare the fields. Inside
struct Dog, add aStringfield callednameand ani32field calledage, separated by commas. EveryDogthen owns its own copy of this data. - Set them. In
mainFunction, assign"Rex".to_string()todog.nameand4todog.age. The starter code already declaresdogwithlet mut, and thatmutis what allows the assignments.
The string needs to_string() because a literal such as "Rex" is a borrowed &str, while the field holds an owned String.
The solution in Rust
struct Dog {
name: String,
age: i32,
}
fn mainFunction() {
let mut dog = Dog {
name: String::new(),
age: 0,
};
dog.name = "Rex".to_string();
dog.age = 4;
println!("{} is {} years old", dog.name, dog.age);
}
Running mainFunction prints:
Rex is 4 years old
Rust details that matter here:
- Mutability belongs to the binding. Without
mutondog,dog.age = 4is rejected: "cannot assign todog.age, asdogis not declared as mutable". There is no per-fieldmut: the whole value is either changeable or not. - Types are checked.
dog.age = "4"anddog.name = "Rex"are both mismatched-type errors, becauseageis ani32andnameaString. A misspelt field, likedog.nam, is reported as no such field on the typeDog. - Visibility. Fields are private by default, which means private to the module that defines the struct. Code in the same file and module, like this exercise, can use them freely, so no
pubis needed. To let code in other modules read or write a field, putpubbefore it:pub name: String. - Fields own their data. The
Stringinsidedogis moved or dropped together withdog. Pulling a field out with a destructuring pattern,let Dog { name, age } = dog;, takes the owned values out of the struct. - Formatting. Each
{}inprintln!takes the next argument. BothStringandi32can be printed with{}.
Common mistakes
- Leaving out
mut. The binding must be declaredlet mutto change a field. - Assigning a literal to a
Stringfield. Useto_string(),String::from("Rex")or.into(). - Missing commas between fields. Each field needs a comma after it, except that the last comma is optional.
- Using a field name without the instance. Writing
name = ...by itself refers to a variable calledname. Fields are reached asdog.name. - Forgetting a field in the struct expression. Every field needs a value when the instance is created.
Key takeaways
- A field is declared as
name: Typeinside the struct, and every instance has its own copy. - The dot operator reads and writes fields.
- Changing a field needs a
let mutbinding. - A struct that owns text stores a
String, so convert literals withto_string(). - Fields are private to their module unless marked
pub.
