Skip to content

Commit c685c67

Browse files
committed
Use infallible conversion to teach From trait
1 parent 4f1a440 commit c685c67

3 files changed

Lines changed: 58 additions & 216 deletions

File tree

exercises/23_conversions/from_into.rs

Lines changed: 27 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -2,129 +2,51 @@
22
// implemented, an implementation of `Into` is automatically provided.
33
// You can read more about it in the documentation:
44
// https://doc.rust-lang.org/std/convert/trait.From.html
5+
//
6+
// Frank the fairy would like to buy some truffles from Grace the gnome, a
7+
// world-renowned chocolatier. The truffles are priced in GnomeCoin though, and
8+
// Frank only has FairyCredit. Help Frank by providing a `From` implementation
9+
// to convert his FairyCredit to GnomeCoin. At the current exchange rate, one
10+
// FairyCredit is valued at 100 GnomeCoin.
511

612
#[derive(Debug)]
7-
struct Person {
8-
name: String,
9-
age: u8,
10-
}
13+
struct FairyCredit(u32);
1114

12-
// We implement the Default trait to use it as a fallback when the provided
13-
// string is not convertible into a `Person` object.
14-
impl Default for Person {
15-
fn default() -> Self {
16-
Self {
17-
name: String::from("John"),
18-
age: 30,
19-
}
20-
}
21-
}
15+
#[derive(Debug, PartialEq)]
16+
struct GnomeCoin(u64);
2217

23-
// TODO: Complete this `From` implementation to be able to parse a `Person`
24-
// out of a string in the form of "Mark,20".
25-
// Note that you'll need to parse the age component into a `u8` with something
26-
// like `"4".parse::<u8>()`.
27-
//
28-
// Steps:
29-
// 1. Split the given string on the commas present in it.
30-
// 2. If the split operation returns less or more than 2 elements, return the
31-
// default of `Person`.
32-
// 3. Use the first element from the split operation as the name.
33-
// 4. If the name is empty, return the default of `Person`.
34-
// 5. Parse the second element from the split operation into a `u8` as the age.
35-
// 6. If parsing the age fails, return the default of `Person`.
36-
impl From<&str> for Person {
37-
fn from(s: &str) -> Self {}
18+
impl From<FairyCredit> for GnomeCoin {
19+
// TODO: implement From<FairyCredit> for GnomeCoin
3820
}
3921

22+
// Note that we shouldn't provide the opposite conversion: from GnomeCoin to
23+
// FairyCredits. That's because less than 100 GnomeCoins cannot be represented
24+
// as FairyCredits, which would make the conversion lossy. The `From` trait is
25+
// only appropriate for infallible and lossless conversions.
26+
4027
fn main() {
4128
// Use the `from` function.
42-
let p1 = Person::from("Mark,20");
43-
println!("{p1:?}");
29+
let g1 = GnomeCoin::from(FairyCredit(12));
30+
println!("{g1:?}");
4431

45-
// Since `From` is implemented for Person, we are able to use `Into`.
46-
let p2: Person = "Gerald,70".into();
47-
println!("{p2:?}");
32+
// Since `From` is implemented for GnomeCoin, we are able to use `Into`.
33+
let g2: GnomeCoin = FairyCredit(9).into();
34+
println!("{g2:?}");
4835
}
4936

5037
#[cfg(test)]
5138
mod tests {
5239
use super::*;
5340

5441
#[test]
55-
fn test_default() {
56-
let dp = Person::default();
57-
assert_eq!(dp.name, "John");
58-
assert_eq!(dp.age, 30);
59-
}
60-
61-
#[test]
62-
fn test_bad_convert() {
63-
let p = Person::from("");
64-
assert_eq!(p.name, "John");
65-
assert_eq!(p.age, 30);
66-
}
67-
68-
#[test]
69-
fn test_good_convert() {
70-
let p = Person::from("Mark,20");
71-
assert_eq!(p.name, "Mark");
72-
assert_eq!(p.age, 20);
73-
}
74-
75-
#[test]
76-
fn test_bad_age() {
77-
let p = Person::from("Mark,twenty");
78-
assert_eq!(p.name, "John");
79-
assert_eq!(p.age, 30);
80-
}
81-
82-
#[test]
83-
fn test_missing_comma_and_age() {
84-
let p: Person = Person::from("Mark");
85-
assert_eq!(p.name, "John");
86-
assert_eq!(p.age, 30);
87-
}
88-
89-
#[test]
90-
fn test_missing_age() {
91-
let p: Person = Person::from("Mark,");
92-
assert_eq!(p.name, "John");
93-
assert_eq!(p.age, 30);
94-
}
95-
96-
#[test]
97-
fn test_missing_name() {
98-
let p: Person = Person::from(",1");
99-
assert_eq!(p.name, "John");
100-
assert_eq!(p.age, 30);
101-
}
102-
103-
#[test]
104-
fn test_missing_name_and_age() {
105-
let p: Person = Person::from(",");
106-
assert_eq!(p.name, "John");
107-
assert_eq!(p.age, 30);
108-
}
109-
110-
#[test]
111-
fn test_missing_name_and_invalid_age() {
112-
let p: Person = Person::from(",one");
113-
assert_eq!(p.name, "John");
114-
assert_eq!(p.age, 30);
115-
}
116-
117-
#[test]
118-
fn test_trailing_comma() {
119-
let p: Person = Person::from("Mike,32,");
120-
assert_eq!(p.name, "John");
121-
assert_eq!(p.age, 30);
42+
fn test_from() {
43+
let g = GnomeCoin::from(FairyCredit(12));
44+
assert_eq!(g, GnomeCoin(1200));
12245
}
12346

12447
#[test]
125-
fn test_trailing_comma_and_some_string() {
126-
let p: Person = Person::from("Mike,32,dog");
127-
assert_eq!(p.name, "John");
128-
assert_eq!(p.age, 30);
48+
fn test_into() {
49+
let g: GnomeCoin = FairyCredit(9).into();
50+
assert_eq!(g, GnomeCoin(900));
12951
}
13052
}

rustlings-macros/info.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1165,7 +1165,9 @@ Use the `as` operator to cast one of the operands in the last line of the
11651165
name = "from_into"
11661166
dir = "23_conversions"
11671167
hint = """
1168-
Follow the steps provided right before the `From` implementation."""
1168+
Implement From<FairyCredit> for GnomeCoin. Check the documentation of `From` to
1169+
learn about its required items:
1170+
https://doc.rust-lang.org/std/convert/trait.From.html"""
11691171

11701172
[[exercises]]
11711173
name = "from_str"

solutions/23_conversions/from_into.rs

Lines changed: 28 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -2,135 +2,53 @@
22
// implemented, an implementation of `Into` is automatically provided.
33
// You can read more about it in the documentation:
44
// https://doc.rust-lang.org/std/convert/trait.From.html
5+
//
6+
// Frank the fairy would like to buy some truffles from Grace the gnome, a
7+
// world-renowned chocolatier. The truffles are priced in GnomeCoin though, and
8+
// Frank only has FairyCredit. Help Frank by providing a `From` implementation
9+
// to convert his FairyCredit to GnomeCoin. At the current exchange rate, one
10+
// FairyCredit is valued at 100 GnomeCoin.
511

612
#[derive(Debug)]
7-
struct Person {
8-
name: String,
9-
age: u8,
10-
}
11-
12-
// We implement the Default trait to use it as a fallback when the provided
13-
// string is not convertible into a `Person` object.
14-
impl Default for Person {
15-
fn default() -> Self {
16-
Self {
17-
name: String::from("John"),
18-
age: 30,
19-
}
20-
}
21-
}
22-
23-
impl From<&str> for Person {
24-
fn from(s: &str) -> Self {
25-
let mut split = s.split(',');
26-
let (Some(name), Some(age), None) = (split.next(), split.next(), split.next()) else {
27-
// ^^^^ there should be no third element
28-
return Self::default();
29-
};
13+
struct FairyCredit(u32);
3014

31-
if name.is_empty() {
32-
return Self::default();
33-
}
15+
#[derive(Debug, PartialEq)]
16+
struct GnomeCoin(u64);
3417

35-
let Ok(age) = age.parse() else {
36-
return Self::default();
37-
};
38-
39-
Self {
40-
name: name.into(),
41-
age,
42-
}
18+
impl From<FairyCredit> for GnomeCoin {
19+
fn from(value: FairyCredit) -> Self {
20+
Self(value.0 as u64 * 100)
4321
}
4422
}
4523

24+
// Note that we shouldn't provide the opposite conversion: from GnomeCoin to
25+
// FairyCredits. That's because less than 100 GnomeCoins cannot be represented
26+
// as FairyCredits, which would make the conversion lossy. The `From` trait is
27+
// only appropriate for infallible and lossless conversions.
28+
4629
fn main() {
4730
// Use the `from` function.
48-
let p1 = Person::from("Mark,20");
49-
println!("{p1:?}");
31+
let g1 = GnomeCoin::from(FairyCredit(12));
32+
println!("{g1:?}");
5033

51-
// Since `From` is implemented for Person, we are able to use `Into`.
52-
let p2: Person = "Gerald,70".into();
53-
println!("{p2:?}");
34+
// Since `From` is implemented for GnomeCoin, we are able to use `Into`.
35+
let g2: GnomeCoin = FairyCredit(9).into();
36+
println!("{g2:?}");
5437
}
5538

5639
#[cfg(test)]
5740
mod tests {
5841
use super::*;
5942

6043
#[test]
61-
fn test_default() {
62-
let dp = Person::default();
63-
assert_eq!(dp.name, "John");
64-
assert_eq!(dp.age, 30);
65-
}
66-
67-
#[test]
68-
fn test_bad_convert() {
69-
let p = Person::from("");
70-
assert_eq!(p.name, "John");
71-
assert_eq!(p.age, 30);
72-
}
73-
74-
#[test]
75-
fn test_good_convert() {
76-
let p = Person::from("Mark,20");
77-
assert_eq!(p.name, "Mark");
78-
assert_eq!(p.age, 20);
79-
}
80-
81-
#[test]
82-
fn test_bad_age() {
83-
let p = Person::from("Mark,twenty");
84-
assert_eq!(p.name, "John");
85-
assert_eq!(p.age, 30);
86-
}
87-
88-
#[test]
89-
fn test_missing_comma_and_age() {
90-
let p: Person = Person::from("Mark");
91-
assert_eq!(p.name, "John");
92-
assert_eq!(p.age, 30);
93-
}
94-
95-
#[test]
96-
fn test_missing_age() {
97-
let p: Person = Person::from("Mark,");
98-
assert_eq!(p.name, "John");
99-
assert_eq!(p.age, 30);
100-
}
101-
102-
#[test]
103-
fn test_missing_name() {
104-
let p: Person = Person::from(",1");
105-
assert_eq!(p.name, "John");
106-
assert_eq!(p.age, 30);
107-
}
108-
109-
#[test]
110-
fn test_missing_name_and_age() {
111-
let p: Person = Person::from(",");
112-
assert_eq!(p.name, "John");
113-
assert_eq!(p.age, 30);
114-
}
115-
116-
#[test]
117-
fn test_missing_name_and_invalid_age() {
118-
let p: Person = Person::from(",one");
119-
assert_eq!(p.name, "John");
120-
assert_eq!(p.age, 30);
121-
}
122-
123-
#[test]
124-
fn test_trailing_comma() {
125-
let p: Person = Person::from("Mike,32,");
126-
assert_eq!(p.name, "John");
127-
assert_eq!(p.age, 30);
44+
fn test_from() {
45+
let g = GnomeCoin::from(FairyCredit(12));
46+
assert_eq!(g, GnomeCoin(1200));
12847
}
12948

13049
#[test]
131-
fn test_trailing_comma_and_some_string() {
132-
let p: Person = Person::from("Mike,32,dog");
133-
assert_eq!(p.name, "John");
134-
assert_eq!(p.age, 30);
50+
fn test_into() {
51+
let g: GnomeCoin = FairyCredit(9).into();
52+
assert_eq!(g, GnomeCoin(900));
13553
}
13654
}

0 commit comments

Comments
 (0)