- Structs are custom data types that let you name/package together multiple related values that make up a meaningful group
- Analogous to an object’s data attributes in OOP
struct User {
username: String,
email: String,
sign_in_count: u64,
}
fn build_user(email: String, username: String) -> User {
let user1 = User {
email,
username,
active: true,
sign_in_count: 1
}
let user2 = User{
email: String::from("krishna.verma@gmail.com"),
...user1
}
}
- Bottom-most code shows the struct update syntax, which allows us to create new struct instances that only update part of the code (basically just a shortcut)
user1
code shows the field init shorthand, which is particularly useful when some of our fields are parameters- Either the entire instance must be mutable, or none of it (initialized with
let mut user1
orlet user1
) - We want instances of our structs to own all of their data, so we use datatypes like
String
vs. references like&str
- In Chapter 10, we discuss how to handle situations where we do want to use references using lifetimes
- Tuple structs are a construct that lets us create structs from tuples such that we can differentiate between them by name
- Initialized like
struct Color(i32, i32, i32)
- Can access elements with a
.
followed by the index
- Initialized like
- We can’t print out our struct by default because Rust doesn’t really know how it should present them, so we use the
#[derive(Debug)]
syntax, as shown below:
#[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{:?}
for larger structs that we want presented as multi-line outputs - Now we introduce an OOP-like way for us to specify functions that can be performed on a certain class
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
- Here,
impl Rectangle
indicates that all of the functions that follow are implemented on theRectangle
struct- Then, in those functions, we can pass in either
&self, &mut self, self
, the latter of which we wouldn’t really use unless we’re transforming it into a different object and want to prevent the caller from using the original instance after the fact
- Then, in those functions, we can pass in either
- For functions, Rust implements automatic referencing/dereferencing such that the method is called properly — we already know which of
&self, &mut self, self
it takes in, so we can reference/dereference it ourselves- Thus
p1.distance(&p2)
and(&p1).distance(&p2)
both evaluate to the same thing in practice - This makes Rust ergonomic!
- Thus
- Associated functions are functions within
impl
blocks that don’t takeself
as a parameter- Generally used as constructors that return new instances of the struct
- To call associated functions, we use
::
syntax, because they are name-spaced by the struct (we’ll discuss name-spacing/modules more in Chapter 7)
References
- Chapter 5 of The Rust Programming Language by Steve Nichols and Nicole Klabnick.