-
Notifications
You must be signed in to change notification settings - Fork 10.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add type conversion and parsing exercises
- Loading branch information
Showing
7 changed files
with
302 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
### Type conversions | ||
|
||
|
||
Rust offers a multitude of ways to convert a value of a given type into another type. | ||
|
||
The simplest form of type conversion is a type cast expression. It is denoted with the binary operator `as`. For instance, `println!("{}", 1 + 1.0);` would not compile, since `1` is an integer while `1.0` is a float. However, `println!("{}", 1 as f32 + 1.0)` should compile. The exercise [`using_as`](using_as.rs) tries to cover this. | ||
|
||
Rust also offers traits that facilitate type conversions upon implementation. These traits can be found under the [`convert`](https://doc.rust-lang.org/std/convert/index.html) module. | ||
The traits are the following: | ||
- `From` and `Into` covered in [`from_into`](from_into.rs) | ||
- `TryFrom` and `TryInto` covered in [`try_from_into`](try_from_into.rs) | ||
- `AsRef` and `AsMut` covered in [`as_ref_mut`](as_ref_mut.rs) | ||
|
||
Furthermore, the `std::str` module offers a trait called [`FromStr`](https://doc.rust-lang.org/std/str/trait.FromStr.html) which helps with converting strings into target types via the `parse` method on strings. If properly implemented for a given type `Person`, then `let p: Person = "Mark,20".parse().unwrap()` should both compile and run without panicking. | ||
|
||
These should be the main ways ***within the standard library*** to convert data into your desired types. | ||
|
||
#### Book Sections | ||
|
||
These are not directly covered in the book, but the standard library has great documentation for [conversions here](https://doc.rust-lang.org/std/convert/index.html). The `FromStr` trait is also covered [here](https://doc.rust-lang.org/std/str/trait.FromStr.html). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
// AsRef and AsMut allow for cheap reference-to-reference conversions. | ||
// Read more about them at https://doc.rust-lang.org/std/convert/trait.AsRef.html | ||
// and https://doc.rust-lang.org/std/convert/trait.AsMut.html, respectively. | ||
|
||
// Obtain the number of bytes (not characters) in the given argument | ||
// Add the AsRef trait appropriately as a trait bound | ||
fn byte_counter<T>(arg: T) -> usize { | ||
arg.as_ref().as_bytes().len() | ||
} | ||
|
||
// Obtain the number of characters (not bytes) in the given argument | ||
// Add the AsRef trait appropriately as a trait bound | ||
fn char_counter<T>(arg: T) -> usize { | ||
arg.as_ref().chars().collect::<Vec<_>>().len() | ||
} | ||
|
||
fn main() { | ||
let s = "Café au lait"; | ||
println!("{}", char_counter(s)); | ||
println!("{}", byte_counter(s)); | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
#[test] | ||
fn different_counts() { | ||
let s = "Café au lait"; | ||
assert_ne!(char_counter(s), byte_counter(s)); | ||
} | ||
fn same_counts() { | ||
let s = "Cafe au lait"; | ||
assert_eq!(char_counter(s), byte_counter(s)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
// The From trait is used for value-to-value conversions. | ||
// If From is implemented correctly for a type, the Into trait should work conversely. | ||
// You can read more about it at https://doc.rust-lang.org/std/convert/trait.From.html | ||
#[derive(Debug)] | ||
struct Person { | ||
name: String, | ||
age: usize, | ||
} | ||
|
||
// We implement the Default trait to use it as a fallback | ||
// when the provided string is not convertible into a Person object | ||
impl Default for Person { | ||
fn default() -> Person { | ||
Person { | ||
name: String::from("John"), | ||
age: 30, | ||
} | ||
} | ||
} | ||
|
||
// Your task is to complete this implementation | ||
// in order for the line `let p = Person::from("Mark,20")` to compile | ||
// Please note that you'll need to parse the age component into a `usize` | ||
// with something like `"4".parse::<usize>()`. The outcome of this needs to | ||
// be handled appropriately. | ||
// | ||
// Steps: | ||
// 1. If the length of the provided string is 0, then return the default of Person | ||
// 2. Split the given string on the commas present in it | ||
// 3. Extract the first element from the split operation and use it as the name | ||
// 4. Extract the other element from the split operation and parse it into a `usize` as the age | ||
// If while parsing the age, something goes wrong, then return the default of Person | ||
// Otherwise, then return an instantiated Person onject with the results | ||
impl From<&str> for Person { | ||
fn from(s: &str) -> Person { | ||
} | ||
} | ||
|
||
fn main() { | ||
// Use the `from` function | ||
let p1 = Person::from("Mark,20"); | ||
// Since From is implemented for Person, we should be able to use Into | ||
let p2: Person = "Gerald,70".into(); | ||
println!("{:?}", p1); | ||
println!("{:?}", p2); | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
#[test] | ||
fn test_default() { | ||
// Test that the default person is 30 year old John | ||
let dp = Person::default(); | ||
assert_eq!(dp.name, "John"); | ||
assert_eq!(dp.age, 30); | ||
} | ||
#[test] | ||
fn test_bad_convert() { | ||
// Test that John is returned when bad string is provided | ||
let p = Person::from(""); | ||
assert_eq!(p.name, "John"); | ||
assert_eq!(p.age, 30); | ||
} | ||
#[test] | ||
fn test_good_convert() { | ||
// Test that "Mark,20" works | ||
let p = Person::from("Mark,20"); | ||
assert_eq!(p.name, "Mark"); | ||
assert_eq!(p.age, 20); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
// This does practically the same thing that TryFrom<&str> does. | ||
// Additionally, upon implementing FromStr, you can use the `parse` method | ||
// on strings to generate an object of the implementor type. | ||
// You can read more about it at https://doc.rust-lang.org/std/str/trait.FromStr.html | ||
use std::str::FromStr; | ||
|
||
#[derive(Debug)] | ||
struct Person { | ||
name: String, | ||
age: usize, | ||
} | ||
|
||
// Steps: | ||
// 1. If the length of the provided string is 0, then return an error | ||
// 2. Split the given string on the commas present in it | ||
// 3. Extract the first element from the split operation and use it as the name | ||
// 4. Extract the other element from the split operation and parse it into a `usize` as the age | ||
// If while parsing the age, something goes wrong, then return an error | ||
// Otherwise, then return a Result of a Person object | ||
impl FromStr for Person { | ||
type Err = String; | ||
fn from_str(s: &str) -> Result<Person, Self::Err> { | ||
} | ||
} | ||
|
||
fn main() { | ||
let p = "Mark,20".parse::<Person>().unwrap(); | ||
println!("{:?}", p); | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
#[test] | ||
fn empty_input() { | ||
assert!("".parse::<Person>().is_err()); | ||
} | ||
#[test] | ||
fn good_input() { | ||
assert!("John,32".parse::<Person>().is_ok()); | ||
} | ||
#[test] | ||
#[should_panic] | ||
fn missing_age() { | ||
"John".parse::<Person>().unwrap(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
// TryFrom is a simple and safe type conversion that may fail in a controlled way under some circumstances. | ||
// Basically, this is the same as From. The main difference is that this should return a Result type | ||
// instead of the target type itself. | ||
// You can read more about it at https://doc.rust-lang.org/std/convert/trait.TryFrom.html | ||
use std::convert::{TryInto, TryFrom}; | ||
|
||
#[derive(Debug)] | ||
struct Person { | ||
name: String, | ||
age: usize, | ||
} | ||
|
||
// Your task is to complete this implementation | ||
// in order for the line `let p = Person::try_from("Mark,20")` to compile | ||
// and return an Ok result of inner type Person. | ||
// Please note that you'll need to parse the age component into a `usize` | ||
// with something like `"4".parse::<usize>()`. The outcome of this needs to | ||
// be handled appropriately. | ||
// | ||
// Steps: | ||
// 1. If the length of the provided string is 0, then return an error | ||
// 2. Split the given string on the commas present in it | ||
// 3. Extract the first element from the split operation and use it as the name | ||
// 4. Extract the other element from the split operation and parse it into a `usize` as the age | ||
// If while parsing the age, something goes wrong, then return an error | ||
// Otherwise, then return a Result of a Person object | ||
impl TryFrom<&str> for Person { | ||
type Error = String; | ||
fn try_from(s: &str) -> Result<Self, Self::Error> { | ||
} | ||
} | ||
|
||
fn main() { | ||
// Use the `from` function | ||
let p1 = Person::try_from("Mark,20"); | ||
// Since From is implemented for Person, we should be able to use Into | ||
let p2: Result<Person, _> = "Gerald,70".try_into(); | ||
println!("{:?}", p1); | ||
println!("{:?}", p2); | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
#[test] | ||
fn test_bad_convert() { | ||
// Test that John is returned when bad string is provided | ||
let p = Person::try_from(""); | ||
assert!(p.is_err()); | ||
} | ||
#[test] | ||
fn test_good_convert() { | ||
// Test that "Mark,20" works | ||
let p = Person::try_from("Mark,20"); | ||
assert!(p.is_ok()); | ||
let p = p.unwrap(); | ||
assert_eq!(p.name, "Mark"); | ||
assert_eq!(p.age, 20); | ||
} | ||
#[test] | ||
#[should_panic] | ||
fn test_panic_empty_input() { | ||
let p: Person = "".try_into().unwrap(); | ||
} | ||
#[test] | ||
#[should_panic] | ||
fn test_panic_bad_age() { | ||
let p = Person::try_from("Mark,twenty").unwrap(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// Type casting in Rust is done via the usage of the `as` operator. | ||
// Please note that the `as` operator is not only used when type casting. | ||
// It also helps with renaming imports. | ||
|
||
// The goal is to make sure that the division does not fail to compile | ||
fn average(values: &[f64]) -> f64 { | ||
let total = values | ||
.iter() | ||
.fold(0.0, |a, b| a + b); | ||
total / values.len() | ||
} | ||
|
||
fn main() { | ||
let values = [3.5, 0.3, 13.0, 11.7]; | ||
println!("{}", average(&values)); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters