Like tuples, the pieces of a struct can be different types.
Unlike with tuples, in a struct you’ll name each piece of data so it’s clear what the values mean.
struct User {
active: bool,
username: String,
email: String,
sign_in_count: u64,
}
fn main() {
let mut user1 = User {
active: true,
username: String::from("someusername123"),
email: String::from("someone@example.com"),
sign_in_count: 1,
};
user1.email = String::from("anotheremail@example.com");
}
fn build_user(email: String, username: String) -> User {
User {
active: true,
username,
email,
sign_in_count: 1,
}
}
let user2 = User {
email: String::from("another@example.com"),
..user1 //must come last
};
//will not work
//println!("{}", user1.username);
let user3 = User {
active: user2.active,
sign_in_count: user2.sign_in_count,
username: String::from("someusername123"),
email: String::from("someone@example.com"),
};
println!("{}", user3.username);
we used the owned String
type rather than the &str
string slice type
because we want each instance of this struct to own all of its data and for that data to be valid for as long as the entire struct is valid
It’s also possible for structs to store references to data owned by something else, but to do so requires the use of lifetimes, see [10-generic types, traits, lifetimes]
struct Color(i32, i32, i32);
struct Point(i32, i32, f32);
fn main() {
let black = Color(0, 0, 0);
let origin = Point(0, 0, 0.1);
}
structs that don’t have any fields!
These are called unit-like structs because they behave similarly to ()
struct AlwaysEqual;
fn main() {
let subject = AlwaysEqual;
}
useful when you need to implement a trait on some type but don’t have any data that you want to store in the type itself
You’ll see in [10-generic types, traits, lifetimes] how to define traits and implement them on any type, including unit-like structs.
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
println!("rect1 is {}", rect1);
}
error[E0277]: `Rectangle` doesn't implement `std::fmt::Display`
by default, the curly brackets tell println!
to use formatting known as Display
with structs, the way println! should format the output is less clear because there are more display possibilities
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
println!("rect1 is {rect1:?}");
}
we can use {:#?}
instead of {:?}
, a bit easier to read
dbg!
macro, which takes ownership of an expression (as opposed to println!, which takes a reference)
dbg!
macro prints to the standard error console stream (stderr
), as opposed to println!
, which prints to the standard output console stream (stdout
). see [12-cli project]Unlike functions, methods are defined within the context of a struct (or an enum [06-enums and patterns] or a trait [17 oopf] object)
their first parameter is always self
, which represents the instance of the struct the method is being called on
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
println!(
"The area of the rectangle is {} square pixels.",
rect1.area()
);
}
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}
Often, but not always, when we give a method the same name as a field we want it to only return the value in the field and do nothing else.
Methods like this are called getters, and Rust does not implement them automatically for struct fields as some other languages do.
Getters are useful because you can make the field private but the method public, and thus enable read-only access to that field as part of the type’s public API.
We will discuss what public and private are and how to designate a field or method as public or private in [07-packages, crates, modules].
impl Rectangle {
fn square(size: u32) -> Self {
Self {
width: size,
height: size,
}
}
}
let sq = Rectangle::square(3)
::
syntax is used for both associated functions and namespaces created by modules. see [07-packages, crates, modules].impl
Blocksstruct
self/cross-referencing// cross referencing
impl Printer {
// A public method in Printer struct that uses an instance of Calculator
pub fn print_sum(&self, calculator: &Calculator, x: i32, y: i32) {
let sum = calculator.add(x, y); // Calling Calculator's method
println!("The sum of {} and {} is: {}", x, y, sum);
}
}