Structs
-
Struct:
- Structs are user-defined types that allow you to group related data together similar to tuples.
- Unlike with tuples, in a struct you’ll name each piece of data so it’s clear what the values mean.
- Adding these names means that structs are more flexible than tuples so you don’t have to rely on the order of the data to specify or access the values of an instance.
- Structs can have associated methods, which are functions tied to the struct.
- Have struct update syntax capabilities using
..
. - Types of struct in Rust are:
- Regular Struct: Fields are explicitly named.
- Tuple Struct: Fields are identified by their position (like a tuple).
- Unit-Like Struct: A struct with no field.
-
Method:
- Methods are similar to functions: we declare them with the fn keyword and a name.
- Unlike functions, methods are defined within the context of a struct (or an enum or a trait object)
- Their first parameter is always self, which represents the instance of the struct the method is being called on.
-
References:
structs1.rs
struct ColorRegularStruct {
// Add the fields that the test `regular_structs` expects.
// What types should the fields have? What are the minimum and maximum values for RGB colors?
red: i32,
green: i32,
blue: i32,
}
struct ColorTupleStruct(i32, i32, i32);
#[derive(Debug)]
struct UnitStruct;
fn main() {
// You can optionally experiment here.
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn regular_structs() {
// Instantiate a regular struct.
let green = ColorRegularStruct {
red: 0,
green: 255,
blue: 0,
};
assert_eq!(green.red, 0);
assert_eq!(green.green, 255);
assert_eq!(green.blue, 0);
}
#[test]
fn tuple_structs() {
// Instantiate a tuple struct.
let green = ColorTupleStruct(0, 255, 0);
assert_eq!(green.0, 0);
assert_eq!(green.1, 255);
assert_eq!(green.2, 0);
}
#[test]
fn unit_structs() {
// Instantiate a unit struct.
let unit_struct = UnitStruct;
let message = format!("{unit_struct:?}s are fun!");
assert_eq!(message, "UnitStructs are fun!");
}
}
-
In this exercise we will define 3 types of struct.
-
First is regular struct.
-
Based on the test, it expect 3 fields
red
,green
, andblue
so we can define it like this:struct ColorRegularStruct {
red: i32,
green: i32,
blue: i32,
}
// and use it like this
let green = ColorRegularStruct {
red: 0,
green: 255,
blue: 0,
};
-
-
Second is tuple struct.
-
Based on the test, it also expect 3 fields, so we define it like this:
struct ColorTupleStruct(i32, i32, i32);
// and use it like this
let green = ColorTupleStruct(0, 255, 0);
-
-
And last is unit-like struct.
-
It's already defined and we just need to instantiate it like this:
let unit_struct = UnitStruct;
-
structs2.rs
#[derive(Debug)]
struct Order {
name: String,
year: u32,
made_by_phone: bool,
made_by_mobile: bool,
made_by_email: bool,
item_number: u32,
count: u32,
}
fn create_order_template() -> Order {
Order {
name: String::from("Bob"),
year: 2019,
made_by_phone: false,
made_by_mobile: false,
made_by_email: true,
item_number: 123,
count: 0,
}
}
fn main() {
// You can optionally experiment here.
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn your_order() {
let order_template = create_order_template();
// Create your own order using the update syntax and template above!
let your_order = Order {
name: String::from("Hacker in Rust"),
count: 1,
..order_template
};
assert_eq!(your_order.name, "Hacker in Rust");
assert_eq!(your_order.year, order_template.year);
assert_eq!(your_order.made_by_phone, order_template.made_by_phone);
assert_eq!(your_order.made_by_mobile, order_template.made_by_mobile);
assert_eq!(your_order.made_by_email, order_template.made_by_email);
assert_eq!(your_order.item_number, order_template.item_number);
assert_eq!(your_order.count, 1);
}
}
-
In this exercise we need to create an
Order
fromorder_template
by using struct update syntax. -
So based on test we only need change
name
andcount
so we do it like this:let your_order = Order {
name: String::from("Hacker in Rust"),
count: 1,
..order_template
}; -
You can read more about it here: Creating Instances from Other Instances with Struct Update Syntax.
structs3.rs
// Structs contain data, but can also have logic. In this exercise, we have
// defined the `Package` struct, and we want to test some logic attached to it.
#[derive(Debug)]
struct Package {
sender_country: String,
recipient_country: String,
weight_in_grams: u32,
}
impl Package {
fn new(sender_country: String, recipient_country: String, weight_in_grams: u32) -> Self {
if weight_in_grams < 10 {
// This isn't how you should handle errors in Rust, but we will
// learn about error handling later.
panic!("Can't ship a package with weight below 10 grams");
}
Self {
sender_country,
recipient_country,
weight_in_grams,
}
}
// Add the correct return type to the function signature.
fn is_international(&self) -> bool {
self.sender_country != self.recipient_country
}
// Add the correct return type to the function signature.
fn get_fees(&self, cents_per_gram: u32) -> u32 {
self.weight_in_grams * cents_per_gram
}
}
fn main() {
// You can optionally experiment here.
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[should_panic]
fn fail_creating_weightless_package() {
let sender_country = String::from("Spain");
let recipient_country = String::from("Austria");
Package::new(sender_country, recipient_country, 5);
}
#[test]
fn create_international_package() {
let sender_country = String::from("Spain");
let recipient_country = String::from("Russia");
let package = Package::new(sender_country, recipient_country, 1200);
assert!(package.is_international());
}
#[test]
fn create_local_package() {
let sender_country = String::from("Canada");
let recipient_country = sender_country.clone();
let package = Package::new(sender_country, recipient_country, 1200);
assert!(!package.is_international());
}
#[test]
fn calculate_transport_fees() {
let sender_country = String::from("Spain");
let recipient_country = String::from("Spain");
let cents_per_gram = 3;
let package = Package::new(sender_country, recipient_country, 1500);
assert_eq!(package.get_fees(cents_per_gram), 4500);
assert_eq!(package.get_fees(cents_per_gram * 2), 9000);
}
}
-
In this exercise we need to fix two method.
-
First is
is_international
, based on the test ifsender_country
andrecipient_country
is different the it considered as international, and expected return type isbool
.fn is_international(&self) -> bool {
self.sender_country != self.recipient_country
} -
Second is
get_fees
, based on the test the formula of fees isweight_in_grams * cents_per_gram
and expected return type isu32
.fn get_fees(&self, cents_per_gram: u32) -> u32 {
self.weight_in_grams * cents_per_gram
}