Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
### Changed

- `vecs2`: Removed the use of `map` and `collect`, which are only taught later.
- `structs3`: Rewrote the exercise to make users type method syntax themselves.

## 6.5.0 (2025-08-21)

Expand Down
103 changes: 37 additions & 66 deletions exercises/07_structs/structs3.rs
Original file line number Diff line number Diff line change
@@ -1,37 +1,33 @@
// 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.
// defined the `Fireworks` struct and a couple of functions that work with it.
// Turn these free-standing functions into methods and associated functions
// to express that relationship more clearly in the code.

#![deny(clippy::use_self)] // practice using the `Self` type

#[derive(Debug)]
struct Package {
sender_country: String,
recipient_country: String,
weight_in_grams: u32,
struct Fireworks {
rockets: usize,
}

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,
}
}
// TODO: Turn this function into an associated function on `Fireworks`.
fn new_fireworks() -> Fireworks {
Fireworks { rockets: 0 }
}

// TODO: Add the correct return type to the function signature.
fn is_international(&self) {
// TODO: Read the tests that use this method to find out when a package
// is considered international.
}
// TODO: Turn this function into a method on `Fireworks`.
fn add_rockets(fireworks: &mut Fireworks, rockets: usize) {
fireworks.rockets += rockets
}

// TODO: Add the correct return type to the function signature.
fn get_fees(&self, cents_per_gram: u32) {
// TODO: Calculate the package's fees.
// TODO: Turn this function into a method on `Fireworks`.
fn start(fireworks: Fireworks) -> String {
if fireworks.rockets < 5 {
String::from("small")
} else if fireworks.rockets < 20 {
String::from("medium")
} else {
String::from("big")
}
}

Expand All @@ -44,44 +40,19 @@ 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);
fn start_some_fireworks() {
let mut f = Fireworks::new();
f.add_rockets(3);
assert_eq!(f.start(), "small");

let mut f = Fireworks::new();
f.add_rockets(15);
assert_eq!(f.start(), "medium");

let mut f = Fireworks::new();
f.add_rockets(100);
assert_eq!(Fireworks::start(f), "big");
// We don't use method syntax in the last test to ensure the `start`
// function takes ownership of the fireworks.
}
}
9 changes: 4 additions & 5 deletions rustlings-macros/info.toml
Original file line number Diff line number Diff line change
Expand Up @@ -417,11 +417,10 @@ https://doc.rust-lang.org/book/ch05-01-defining-structs.html#creating-instances-
name = "structs3"
dir = "07_structs"
hint = """
For `is_international`: What makes a package international? Seems related to
the places it goes through right?

For `get_fees`: This method takes an additional argument, is there a field in
the `Package` struct that this relates to?
Methods and associated functions are both declared in an `impl MyType {}`
block. Methods have a `self`, `&self` or `&mut self` parameter, where `self`
implicitly has the type of the impl block. Associated functions do not have
a `self` parameter.

Have a look in The Book to find out more about method implementations:
https://doc.rust-lang.org/book/ch05-03-method-syntax.html"""
Expand Down
93 changes: 31 additions & 62 deletions solutions/07_structs/structs3.rs
Original file line number Diff line number Diff line change
@@ -1,33 +1,27 @@
#![deny(clippy::use_self)] // practice using the `Self` type

#[derive(Debug)]
struct Package {
sender_country: String,
recipient_country: String,
weight_in_grams: u32,
struct Fireworks {
rockets: usize,
}

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,
}
impl Fireworks {
fn new() -> Self {
Self { rockets: 0 }
}

fn is_international(&self) -> bool {
// ^^^^^^^ added
self.sender_country != self.recipient_country
fn add_rockets(&mut self, rockets: usize) {
self.rockets += rockets
}

fn get_fees(&self, cents_per_gram: u32) -> u32 {
// ^^^^^^ added
self.weight_in_grams * cents_per_gram
fn start(self) -> String {
if self.rockets < 5 {
String::from("small")
} else if self.rockets < 20 {
String::from("medium")
} else {
String::from("big")
}
}
}

Expand All @@ -40,44 +34,19 @@ 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);
fn start_some_fireworks() {
let mut f = Fireworks::new();
f.add_rockets(3);
assert_eq!(f.start(), "small");

let mut f = Fireworks::new();
f.add_rockets(15);
assert_eq!(f.start(), "medium");

let mut f = Fireworks::new();
f.add_rockets(100);
assert_eq!(Fireworks::start(f), "big");
// We don't use method syntax in the last test to ensure the `start`
// function takes ownership of the fireworks.
}
}
Loading