Skip to content

Commit 99e0c13

Browse files
committed
Make users type method syntax themselves in structs3
closes #2286
1 parent f80fbca commit 99e0c13

File tree

4 files changed

+73
-133
lines changed

4 files changed

+73
-133
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
### Changed
44

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

78
## 6.5.0 (2025-08-21)
89

exercises/07_structs/structs3.rs

Lines changed: 37 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,33 @@
11
// Structs contain data, but can also have logic. In this exercise, we have
2-
// defined the `Package` struct, and we want to test some logic attached to it.
2+
// defined the `Fireworks` struct and a couple of functions that work with it.
3+
// Turn these free-standing functions into methods and associated functions
4+
// to express that relationship more clearly in the code.
5+
6+
#![deny(clippy::use_self)] // practice using the `Self` type
37

48
#[derive(Debug)]
5-
struct Package {
6-
sender_country: String,
7-
recipient_country: String,
8-
weight_in_grams: u32,
9+
struct Fireworks {
10+
rockets: usize,
911
}
1012

11-
impl Package {
12-
fn new(sender_country: String, recipient_country: String, weight_in_grams: u32) -> Self {
13-
if weight_in_grams < 10 {
14-
// This isn't how you should handle errors in Rust, but we will
15-
// learn about error handling later.
16-
panic!("Can't ship a package with weight below 10 grams");
17-
}
18-
19-
Self {
20-
sender_country,
21-
recipient_country,
22-
weight_in_grams,
23-
}
24-
}
13+
// TODO: Turn this function into an associated function on `Fireworks`.
14+
fn new_fireworks() -> Fireworks {
15+
Fireworks { rockets: 0 }
16+
}
2517

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

32-
// TODO: Add the correct return type to the function signature.
33-
fn get_fees(&self, cents_per_gram: u32) {
34-
// TODO: Calculate the package's fees.
23+
// TODO: Turn this function into a method on `Fireworks`.
24+
fn start(fireworks: Fireworks) -> String {
25+
if fireworks.rockets < 5 {
26+
String::from("small")
27+
} else if fireworks.rockets < 20 {
28+
String::from("medium")
29+
} else {
30+
String::from("big")
3531
}
3632
}
3733

@@ -44,44 +40,19 @@ mod tests {
4440
use super::*;
4541

4642
#[test]
47-
#[should_panic]
48-
fn fail_creating_weightless_package() {
49-
let sender_country = String::from("Spain");
50-
let recipient_country = String::from("Austria");
51-
52-
Package::new(sender_country, recipient_country, 5);
53-
}
54-
55-
#[test]
56-
fn create_international_package() {
57-
let sender_country = String::from("Spain");
58-
let recipient_country = String::from("Russia");
59-
60-
let package = Package::new(sender_country, recipient_country, 1200);
61-
62-
assert!(package.is_international());
63-
}
64-
65-
#[test]
66-
fn create_local_package() {
67-
let sender_country = String::from("Canada");
68-
let recipient_country = sender_country.clone();
69-
70-
let package = Package::new(sender_country, recipient_country, 1200);
71-
72-
assert!(!package.is_international());
73-
}
74-
75-
#[test]
76-
fn calculate_transport_fees() {
77-
let sender_country = String::from("Spain");
78-
let recipient_country = String::from("Spain");
79-
80-
let cents_per_gram = 3;
81-
82-
let package = Package::new(sender_country, recipient_country, 1500);
83-
84-
assert_eq!(package.get_fees(cents_per_gram), 4500);
85-
assert_eq!(package.get_fees(cents_per_gram * 2), 9000);
43+
fn start_some_fireworks() {
44+
let mut f = Fireworks::new();
45+
f.add_rockets(3);
46+
assert_eq!(f.start(), "small");
47+
48+
let mut f = Fireworks::new();
49+
f.add_rockets(15);
50+
assert_eq!(f.start(), "medium");
51+
52+
let mut f = Fireworks::new();
53+
f.add_rockets(100);
54+
assert_eq!(Fireworks::start(f), "big");
55+
// We don't use method syntax in the last test to ensure the `start`
56+
// function takes ownership of the fireworks.
8657
}
8758
}

rustlings-macros/info.toml

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -417,11 +417,10 @@ https://doc.rust-lang.org/book/ch05-01-defining-structs.html#creating-instances-
417417
name = "structs3"
418418
dir = "07_structs"
419419
hint = """
420-
For `is_international`: What makes a package international? Seems related to
421-
the places it goes through right?
422-
423-
For `get_fees`: This method takes an additional argument, is there a field in
424-
the `Package` struct that this relates to?
420+
Methods and associated functions are both declared in an `impl MyType {}`
421+
block. Methods have a `self`, `&self` or `&mut self` parameter, where `self`
422+
implicitly has the type of the impl block. Associated functions do not have
423+
a `self` parameter.
425424
426425
Have a look in The Book to find out more about method implementations:
427426
https://doc.rust-lang.org/book/ch05-03-method-syntax.html"""

solutions/07_structs/structs3.rs

Lines changed: 31 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,27 @@
1+
#![deny(clippy::use_self)] // practice using the `Self` type
2+
13
#[derive(Debug)]
2-
struct Package {
3-
sender_country: String,
4-
recipient_country: String,
5-
weight_in_grams: u32,
4+
struct Fireworks {
5+
rockets: usize,
66
}
77

8-
impl Package {
9-
fn new(sender_country: String, recipient_country: String, weight_in_grams: u32) -> Self {
10-
if weight_in_grams < 10 {
11-
// This isn't how you should handle errors in Rust, but we will
12-
// learn about error handling later.
13-
panic!("Can't ship a package with weight below 10 grams");
14-
}
15-
16-
Self {
17-
sender_country,
18-
recipient_country,
19-
weight_in_grams,
20-
}
8+
impl Fireworks {
9+
fn new() -> Self {
10+
Self { rockets: 0 }
2111
}
2212

23-
fn is_international(&self) -> bool {
24-
// ^^^^^^^ added
25-
self.sender_country != self.recipient_country
13+
fn add_rockets(&mut self, rockets: usize) {
14+
self.rockets += rockets
2615
}
2716

28-
fn get_fees(&self, cents_per_gram: u32) -> u32 {
29-
// ^^^^^^ added
30-
self.weight_in_grams * cents_per_gram
17+
fn start(self) -> String {
18+
if self.rockets < 5 {
19+
String::from("small")
20+
} else if self.rockets < 20 {
21+
String::from("medium")
22+
} else {
23+
String::from("big")
24+
}
3125
}
3226
}
3327

@@ -40,44 +34,19 @@ mod tests {
4034
use super::*;
4135

4236
#[test]
43-
#[should_panic]
44-
fn fail_creating_weightless_package() {
45-
let sender_country = String::from("Spain");
46-
let recipient_country = String::from("Austria");
47-
48-
Package::new(sender_country, recipient_country, 5);
49-
}
50-
51-
#[test]
52-
fn create_international_package() {
53-
let sender_country = String::from("Spain");
54-
let recipient_country = String::from("Russia");
55-
56-
let package = Package::new(sender_country, recipient_country, 1200);
57-
58-
assert!(package.is_international());
59-
}
60-
61-
#[test]
62-
fn create_local_package() {
63-
let sender_country = String::from("Canada");
64-
let recipient_country = sender_country.clone();
65-
66-
let package = Package::new(sender_country, recipient_country, 1200);
67-
68-
assert!(!package.is_international());
69-
}
70-
71-
#[test]
72-
fn calculate_transport_fees() {
73-
let sender_country = String::from("Spain");
74-
let recipient_country = String::from("Spain");
75-
76-
let cents_per_gram = 3;
77-
78-
let package = Package::new(sender_country, recipient_country, 1500);
79-
80-
assert_eq!(package.get_fees(cents_per_gram), 4500);
81-
assert_eq!(package.get_fees(cents_per_gram * 2), 9000);
37+
fn start_some_fireworks() {
38+
let mut f = Fireworks::new();
39+
f.add_rockets(3);
40+
assert_eq!(f.start(), "small");
41+
42+
let mut f = Fireworks::new();
43+
f.add_rockets(15);
44+
assert_eq!(f.start(), "medium");
45+
46+
let mut f = Fireworks::new();
47+
f.add_rockets(100);
48+
assert_eq!(Fireworks::start(f), "big");
49+
// We don't use method syntax in the last test to ensure the `start`
50+
// function takes ownership of the fireworks.
8251
}
8352
}

0 commit comments

Comments
 (0)