In Rust, a struct, short for "structure," is a custom data type that allows you to group together and name multiple related values forming a meaningful unit. If you're familiar with object-oriented languages, you can think of a struct as analogous to an object's data attributes. Unlike tuples, another compound data type in Rust, structs provide named fields for each piece of data, offering greater flexibility and clarity.
Defining and Instantiating Structs
To define a struct, you use the `struct` keyword followed by the struct's name and a list of named fields inside curly brackets. Each field has a name and a data type. For instance, consider the following `User` struct definition:
Rust
struct User {
active: bool,
username: String,
email: String,
sign_in_count: u64,
}
Here, `User` has four fields: `active` of type `bool`, `username` and `email` of type `String`, and `sign_in_count` of type `u64`.
To create an instance of a struct, you specify values for each field using the field names:
Rust
let user1 = User {
active: true,
username: String::from("someusername123"),
email: String::from("someone@example.com"),
sign_in_count: 1,
};
You can access specific values within a struct using dot notation:
Rust
let email = user1.email;
If the struct instance is mutable, you can modify its fields:
Rust
let mut user1 = User {
// fields initialization...
};
user1.email = String::from("anotheremail@example.com");
Field Init Shorthand
Rust provides a shorthand for initializing fields when the parameter names match the struct field names. This eliminates redundancy in code:
Rust
use std::io;
fn build_user(email: String, username: String) -> User {
User {
active: true,
username,
email,
sign_in_count: 1,
}
}
Here, the `build_user` function creates a new `User` instance with specified `email` and `username`, taking advantage of the shorthand notation.
Struct Update Syntax
Struct update syntax allows creating a new instance of a struct by borrowing values from another instance:
Rust
let user2 = User {
email: String::from("another@example.com"),
..user1
};
The `..user1` syntax indicates that the remaining fields not explicitly set in `user2` should have the same values as those in the `user1` instance.
Tuple Structs
Rust supports tuple structs, which resemble tuples but have a named type. Tuple structs are defined using the `struct` keyword followed by the struct name and the types of the fields:
Rust
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);
Although the fields have the same types, instances of `Color` and `Point` are distinct types.
Unit-Like Structs
Unit-like structs have no fields and are defined with just the `struct` keyword followed by the struct name and a semicolon:
Rust
struct AlwaysEqual;
let subject = AlwaysEqual;
These structs are useful when implementing traits without the need for storing data.
Ownership of Struct Data
In the `User` struct example, owned `String` types are used to ensure that each instance owns its data for as long as the struct is valid. Rust also supports structs with references, but this involves lifetimes, a feature.