diff --git a/solutions/type-conversions/from-into.md b/solutions/type-conversions/from-into.md new file mode 100644 index 0000000..7ea8dce --- /dev/null +++ b/solutions/type-conversions/from-into.md @@ -0,0 +1,139 @@ +1. +```rust +fn main() { + // impl From for i32 + let i1:i32 = false.into(); + let i2:i32 = i32::from(false); + assert_eq!(i1, i2); + assert_eq!(i1, 0); + + let i3: u32 = 'a'.into(); + + let s: String = 'a'.into(); +} +``` + +```rust +fn main() { + // impl From for i32 + let i1:i32 = false.into(); + let i2:i32 = i32::from(false); + assert_eq!(i1, i2); + assert_eq!(i1, 0); + + let i3: u32 = 'a' as u32 ; + + let s: String = String::from('a'); +} +``` + +2. +```rust +// From is now included in `std::prelude`, so there is no need to introduce it into the current scope +// use std::convert::From; + +#[derive(Debug)] +struct Number { + value: i32, +} + +impl From for Number { + // IMPLEMENT `from` method + fn from(item: i32) -> Self { + Number { value: item } + } +} + +fn main() { + let num = Number::from(30); + assert_eq!(num.value, 30); + + let num: Number = 30.into(); + assert_eq!(num.value, 30); + + println!("Success!") +} +``` + +3. +```rust +use std::fs; +use std::io; +use std::num; + +enum CliError { + IoError(io::Error), + ParseError(num::ParseIntError), +} + +impl From for CliError { + fn from(error: io::Error) -> Self { + CliError::IoError(error) + } +} + +impl From for CliError { + fn from(error: num::ParseIntError) -> Self { + CliError::ParseError(error) + } +} + +fn open_and_parse_file(file_name: &str) -> Result { + // ? automatically converts io::Error to CliError + let contents = fs::read_to_string(&file_name)?; + // num::ParseIntError -> CliError + let num: i32 = contents.trim().parse()?; + Ok(num) +} + +fn main() { + println!("Success!") +} +``` + +4. +```rust +fn main() { + let n: i16 = 256; + + let n: u8 = match n.try_into() { + Ok(n) => n, + Err(e) => { + println!("there is an error when converting: {:?}, but we catch it", e.to_string()); + 0 + } + }; + + assert_eq!(n, 0); + + println!("Success!") +} +``` + +5. +```rust,editable +#[derive(Debug, PartialEq)] +struct EvenNum(i32); + +impl TryFrom for EvenNum { + type Error = (); + + fn try_from(value: i32) -> Result { + if value % 2 == 0 { + Ok(EvenNum(value)) + } else { + Err(()) + } + } +} + +fn main() { + assert_eq!(EvenNum::try_from(8), Ok(EvenNum(8))); + assert_eq!(EvenNum::try_from(5), Err(())); + + let result: Result = 8i32.try_into(); + assert_eq!(result, Ok(EvenNum(8))); + let result: Result = 5i32.try_into(); + assert_eq!(result, Err(())); +} +``` \ No newline at end of file diff --git a/src/type-conversions/as.md b/src/type-conversions/as.md index 9db5d7e..deb8d58 100644 --- a/src/type-conversions/as.md +++ b/src/type-conversions/as.md @@ -99,5 +99,7 @@ fn main() { unsafe { assert_eq!(std::mem::size_of_val(&*b), __) } + + println!("Success!") } ``` diff --git a/src/type-conversions/from-into.md b/src/type-conversions/from-into.md index fef65e6..72cb63c 100644 --- a/src/type-conversions/from-into.md +++ b/src/type-conversions/from-into.md @@ -1 +1,171 @@ # From/Into +The `From` trait allows for a type to define how to create itself from another type, hence providing a very simple mechanism for converting between several types. + +The `From` and `Into` traits are inherently linked, and this is actually part of its implementation. It means if we write something like this: `impl From for U`, then we can use +`let u: U = U::from(T)` or `let u:U = T.into()`. + +The `Into` trait is simply the reciprocal of the `From` trait. That is, if you have implemented the `From` trait for your type, then the `Into` trait will be automatically implemented for the same type. + +Using the `Into` trait will typically require the type annotations as the compiler is unable to determine this most of the time. + +For example we can easily convert `&str` into `String` : +```rust +fn main() { + let my_str = "hello"; + + // three conversions below all depends on the fact: String implements From<&str>: + let string1 = String::from(my_str); + let string2 = my_str.to_string(); + // explict type annotation is required here + let string3: String = my_str.into(); +} +``` + +because the standard library has already implemented this for us : `impl From<&'_ str> for String` . + +Some implementations of `From` trait can be found [here](https://doc.rust-lang.org/stable/std/convert/trait.From.html#implementors). + +1. 🌟🌟🌟 +```rust,editable + +fn main() { + // impl From for i32 + let i1:i32 = false.into(); + let i2:i32 = i32::from(false); + assert_eq!(i1, i2); + assert_eq!(i1, 0); + + // FIX the error in two ways + // 1. impl From for ? , maybe you should check the docs mentiond above to find the answer + // 2. a keyword from the last chapter + let i3: i32 = 'a'.into(); + + // FIX the error in two ways + let s: String = 'a' as String; + + println!("Success!") +} +``` + +### Implement `From` for custom types +2. 🌟🌟 +```rust,editable + +// From is now included in `std::prelude`, so there is no need to introduce it into the current scope +// use std::convert::From; + +#[derive(Debug)] +struct Number { + value: i32, +} + +impl From for Number { + // IMPLEMENT `from` method +} + +// FILL in the blanks +fn main() { + let num = __(30); + assert_eq!(num.value, 30); + + let num: Number = __; + assert_eq!(num.value, 30); + + println!("Success!") +} +``` + +3. 🌟🌟🌟 When performing error handling it is often useful to implement `From` trait for our own error type. Then we can use `?` to automatically convert the underlying error type to our own error type. +```rust,editable + +use std::fs; +use std::io; +use std::num; + +enum CliError { + IoError(io::Error), + ParseError(num::ParseIntError), +} + +impl From for CliError { + // IMPLEMENT from method +} + +impl From for CliError { + // IMPLEMENT from method +} + +fn open_and_parse_file(file_name: &str) -> Result { + // ? automatically converts io::Error to CliError + let contents = fs::read_to_string(&file_name)?; + // num::ParseIntError -> CliError + let num: i32 = contents.trim().parse()?; + Ok(num) +} + +fn main() { + println!("Success!") +} +``` + + +### TryFrom/TryInto +Similar to `From` and `Into`, `TryFrom` and `TryInto` are generic traits for converting between types. + +Unlike `From/Into`, `TryFrom` and `TryInto` are used for fallible conversions and return a `Result` instead of a plain value. + +4. 🌟🌟 +```rust,editable +// TryFrom and TryInto are included in `std::prelude`, so there is no need to introduce it into the current scope +// use std::convert::TryInto; + +fn main() { + let n: i16 = 256; + + // Into trait has a method `into`, + // hence TryInto has a method ? + let n: u8 = match n.__() { + Ok(n) => n, + Err(e) => { + println!("there is an error when converting: {:?}, but we catch it", e.to_string()); + 0 + } + }; + + assert_eq!(n, __); + + println!("Success!") +} +``` + +5. 🌟🌟🌟 +```rust,editable +#[derive(Debug, PartialEq)] +struct EvenNum(i32); + +impl TryFrom for EvenNum { + type Error = (); + + // IMPLEMENT `try_from` + fn try_from(value: i32) -> Result { + if value % 2 == 0 { + Ok(EvenNum(value)) + } else { + Err(()) + } + } +} + +fn main() { + assert_eq!(EvenNum::try_from(8), Ok(EvenNum(8))); + assert_eq!(EvenNum::try_from(5), Err(())); + + // FILL in the blanks + let result: Result = 8i32.try_into(); + assert_eq!(result, __); + let result: Result = 5i32.try_into(); + assert_eq!(result, __); + + println!("Success!") +} +``` \ No newline at end of file diff --git a/zh-CN/.DS_Store b/zh-CN/.DS_Store index 5172429..d3603fd 100644 Binary files a/zh-CN/.DS_Store and b/zh-CN/.DS_Store differ