mirror of
https://github.com/sunface/rust-by-practice.git
synced 2025-06-24 13:09:40 +00:00
update repo layout
This commit is contained in:
102
en/src/compound-types/array.md
Normal file
102
en/src/compound-types/array.md
Normal file
@ -0,0 +1,102 @@
|
||||
# Array
|
||||
The type of array is `[T; Lengh]`, as you can see, array's lengh is part of their type signature. So their length must be known at compile time.
|
||||
|
||||
For example, you cant initialized an array as below:
|
||||
```rust
|
||||
fn init_arr(n: i32) {
|
||||
let arr = [1; n];
|
||||
}
|
||||
```
|
||||
|
||||
This will cause an error, because the compile have no idea of the exact size of the array in compile time.
|
||||
|
||||
1. 🌟
|
||||
```rust,editable
|
||||
|
||||
fn main() {
|
||||
// fill the blank with proper array type
|
||||
let arr: __ = [1, 2, 3, 4, 5];
|
||||
|
||||
// modify below to make it work
|
||||
assert!(arr.len() == 4);
|
||||
|
||||
println!("Success!")
|
||||
}
|
||||
```
|
||||
|
||||
2. 🌟🌟
|
||||
```rust,editable
|
||||
|
||||
fn main() {
|
||||
// we can ignore parts of the array type or even the whole type, let the compiler infer it for us
|
||||
let arr0 = [1, 2, 3];
|
||||
let arr: [_; 3] = ['a', 'b', 'c'];
|
||||
|
||||
// fill the blank
|
||||
// Arrays are stack allocated, `std::mem::size_of_val` return the bytes which array occupies
|
||||
// A char takes 4 byte in Rust: Unicode char
|
||||
assert!(std::mem::size_of_val(&arr) == __);
|
||||
|
||||
println!("Success!")
|
||||
}
|
||||
```
|
||||
|
||||
3. 🌟 All elements in an array can be initialized to the same value at once.
|
||||
|
||||
```rust,editable
|
||||
|
||||
fn main() {
|
||||
// fill the blank
|
||||
let list: [i32; 100] = __ ;
|
||||
|
||||
assert!(list[0] == 1);
|
||||
assert!(list.len() == 100);
|
||||
|
||||
println!("Success!")
|
||||
}
|
||||
```
|
||||
|
||||
4. 🌟 All elements in an array must be of the same type
|
||||
```rust,editable
|
||||
|
||||
fn main() {
|
||||
// fix the error
|
||||
let _arr = [1, 2, '3'];
|
||||
|
||||
println!("Success!")
|
||||
}
|
||||
```
|
||||
|
||||
5. 🌟 Indexing starts at 0.
|
||||
```rust,editable
|
||||
|
||||
fn main() {
|
||||
let arr = ['a', 'b', 'c'];
|
||||
|
||||
let ele = arr[1]; // only modify this line to make the code work!
|
||||
|
||||
assert!(ele == 'a');
|
||||
|
||||
println!("Success!")
|
||||
}
|
||||
```
|
||||
|
||||
6. 🌟 Out of bounds indexing causes `panic`.
|
||||
```rust,editable
|
||||
|
||||
// fix the error
|
||||
fn main() {
|
||||
let names = [String::from("Sunfei"), "Sunface".to_string()];
|
||||
|
||||
// `get` returns an Option<T>, it's safe to use
|
||||
let name0 = names.get(0).unwrap();
|
||||
|
||||
// but indexing is not safe
|
||||
let _name1 = &names[2];
|
||||
|
||||
println!("Success!")
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
> You can find the solutions [here](https://github.com/sunface/rust-by-practice)(under the solutions path), but only use it when you need it
|
211
en/src/compound-types/enum.md
Normal file
211
en/src/compound-types/enum.md
Normal file
@ -0,0 +1,211 @@
|
||||
# Enum
|
||||
1. 🌟🌟 Enums can be created with explicit discriminator.
|
||||
|
||||
```rust,editable
|
||||
|
||||
// fix the errors
|
||||
enum Number {
|
||||
Zero,
|
||||
One,
|
||||
Two,
|
||||
}
|
||||
|
||||
enum Number1 {
|
||||
Zero = 0,
|
||||
One,
|
||||
Two,
|
||||
}
|
||||
|
||||
// C-like enum
|
||||
enum Number2 {
|
||||
Zero = 0.0,
|
||||
One = 1.0,
|
||||
Two = 2.0,
|
||||
}
|
||||
|
||||
|
||||
fn main() {
|
||||
// a enum variant can be converted to a integer by `as`
|
||||
assert_eq!(Number::One, Number1::One);
|
||||
assert_eq!(Number1::One, Number2::One);
|
||||
|
||||
println!("Success!")
|
||||
}
|
||||
```
|
||||
|
||||
2. 🌟 each enum variant can hold its own data.
|
||||
```rust,editable
|
||||
|
||||
// fill in the blank
|
||||
enum Message {
|
||||
Quit,
|
||||
Move { x: i32, y: i32 },
|
||||
Write(String),
|
||||
ChangeColor(i32, i32, i32),
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let msg1 = Message::Move{__}; // instantiating with x = 1, y = 2
|
||||
let msg2 = Message::Write(__); // instantiating with "hello, world!"
|
||||
|
||||
println!("Success!")
|
||||
}
|
||||
```
|
||||
|
||||
3. 🌟🌟 we can get the data which a enum variant is holding by pattern match
|
||||
```rust,editable
|
||||
|
||||
// fill in the blank and fix the error
|
||||
enum Message {
|
||||
Quit,
|
||||
Move { x: i32, y: i32 },
|
||||
Write(String),
|
||||
ChangeColor(i32, i32, i32),
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let msg = Message::Move{x: 1, y: 2};
|
||||
|
||||
if let Message::Move{__} = msg {
|
||||
assert_eq!(a, b);
|
||||
} else {
|
||||
panic!("NEVER LET THIS RUN!");
|
||||
}
|
||||
|
||||
println!("Success!")
|
||||
}
|
||||
```
|
||||
|
||||
4. 🌟🌟
|
||||
|
||||
```rust,editable
|
||||
|
||||
// fill in the blank and fix the errors
|
||||
enum Message {
|
||||
Quit,
|
||||
Move { x: i32, y: i32 },
|
||||
Write(String),
|
||||
ChangeColor(i32, i32, i32),
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let msgs: __ = [
|
||||
Message::Quit,
|
||||
Message::Move{x:1, y:3},
|
||||
Message::ChangeColor(255,255,0)
|
||||
];
|
||||
|
||||
for msg in msgs {
|
||||
show_message(msg)
|
||||
}
|
||||
}
|
||||
|
||||
fn show_message(msg: Message) {
|
||||
println!("{}", msg);
|
||||
}
|
||||
```
|
||||
|
||||
5. 🌟🌟 As there is no `null` in Rust, we have to use enum `Option<T>` to deal the cases when value is absent.
|
||||
```rust,editable
|
||||
|
||||
// fill in the blank to make the `println` work.
|
||||
// also add some code to prevent the `panic` from running.
|
||||
fn main() {
|
||||
let five = Some(5);
|
||||
let six = plus_one(five);
|
||||
let none = plus_one(None);
|
||||
|
||||
if let __ = six {
|
||||
println!("{}", n);
|
||||
|
||||
println!("Success!")
|
||||
}
|
||||
|
||||
panic!("NEVER LET THIS RUN!");
|
||||
}
|
||||
|
||||
fn plus_one(x: Option<i32>) -> Option<i32> {
|
||||
match x {
|
||||
__ => None,
|
||||
__ => Some(i + 1),
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
6. 🌟🌟🌟🌟 implement a `linked-list` via enums.
|
||||
|
||||
```rust,editable
|
||||
|
||||
use crate::List::*;
|
||||
|
||||
enum List {
|
||||
// Cons: Tuple struct that wraps an element and a pointer to the next node
|
||||
Cons(u32, Box<List>),
|
||||
// Nil: A node that signifies the end of the linked list
|
||||
Nil,
|
||||
}
|
||||
|
||||
// Methods can be attached to an enum
|
||||
impl List {
|
||||
// Create an empty list
|
||||
fn new() -> List {
|
||||
// `Nil` has type `List`
|
||||
Nil
|
||||
}
|
||||
|
||||
// Consume a list, and return the same list with a new element at its front
|
||||
fn prepend(self, elem: u32) -> __ {
|
||||
// `Cons` also has type List
|
||||
Cons(elem, Box::new(self))
|
||||
}
|
||||
|
||||
// Return the length of the list
|
||||
fn len(&self) -> u32 {
|
||||
// `self` has to be matched, because the behavior of this method
|
||||
// depends on the variant of `self`
|
||||
// `self` has type `&List`, and `*self` has type `List`, matching on a
|
||||
// concrete type `T` is preferred over a match on a reference `&T`
|
||||
// after Rust 2018 you can use self here and tail (with no ref) below as well,
|
||||
// rust will infer &s and ref tail.
|
||||
// See https://doc.rust-lang.org/edition-guide/rust-2018/ownership-and-lifetimes/default-match-bindings.html
|
||||
match *self {
|
||||
// Can't take ownership of the tail, because `self` is borrowed;
|
||||
// instead take a reference to the tail
|
||||
Cons(_, ref tail) => 1 + tail.len(),
|
||||
// Base Case: An empty list has zero length
|
||||
Nil => 0
|
||||
}
|
||||
}
|
||||
|
||||
// Return representation of the list as a (heap allocated) string
|
||||
fn stringify(&self) -> String {
|
||||
match *self {
|
||||
Cons(head, __ tail) => {
|
||||
// `format!` is similar to `print!`, but returns a heap
|
||||
// allocated string instead of printing to the console
|
||||
format!("{}, {}", head, tail.__())
|
||||
},
|
||||
Nil => {
|
||||
format!("Nil")
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Create an empty linked list
|
||||
let mut list = List::new();
|
||||
|
||||
// Prepend some elements
|
||||
list = list.prepend(1);
|
||||
list = list.prepend(2);
|
||||
list = list.prepend(3);
|
||||
|
||||
// Show the final state of the list
|
||||
println!("linked list has length: {}", list.len());
|
||||
println!("{}", list.stringify());
|
||||
}
|
||||
```
|
||||
|
||||
> You can find the solutions [here](https://github.com/sunface/rust-by-practice)(under the solutions path), but only use it when you need it
|
6
en/src/compound-types/intro.md
Normal file
6
en/src/compound-types/intro.md
Normal file
@ -0,0 +1,6 @@
|
||||
# Compound Types
|
||||
Learning resources:
|
||||
- English: [Rust Book 4.3, 5.1, 6.1, 8.2](https://doc.rust-lang.org/book/ch04-03-slices.html)
|
||||
- 简体中文: [Rust语言圣经 - 复合类型](https://course.rs/basic/compound-type/intro.html)
|
||||
|
||||
|
100
en/src/compound-types/slice.md
Normal file
100
en/src/compound-types/slice.md
Normal file
@ -0,0 +1,100 @@
|
||||
# Slice
|
||||
Slices are similar to arrays, but their length is not known at compile time, so you can't use slice directly.
|
||||
|
||||
1. 🌟🌟 Here, both `[i32]` and `str` are slice types, but directly using it will cause errors. You have to use the reference of the slice instead: `&[i32]`, `&str`.
|
||||
```rust,editable
|
||||
|
||||
// fix the errors, DON'T add new lines!
|
||||
fn main() {
|
||||
let arr = [1, 2, 3];
|
||||
let s1: [i32] = arr[0..2];
|
||||
|
||||
let s2: str = "hello, world" as str;
|
||||
|
||||
println!("Success!")
|
||||
}
|
||||
```
|
||||
|
||||
A slice reference is a two-word object, for simplicity reasons, from now on we will use slice instead of `slice reference`. The first word is a pointer to the data, and the second word is the length of the slice. The word size is the same as usize, determined by the processor architecture eg 64 bits on an x86-64. Slices can be used to borrow a section of an array, and have the type signature `&[T]`.
|
||||
|
||||
2. 🌟🌟🌟
|
||||
```rust,editable
|
||||
|
||||
fn main() {
|
||||
let arr: [char; 3] = ['中', '国', '人'];
|
||||
|
||||
let slice = &arr[..2];
|
||||
|
||||
// modify '6' to make it work
|
||||
// TIPS: slice( reference ) IS NOT an array, if it is an array, then `assert!` will passed: each of the two UTF-8 chars '中' and '国' occupies 3 bytes, 2 * 3 = 6
|
||||
assert!(std::mem::size_of_val(&slice) == 6);
|
||||
|
||||
println!("Success!")
|
||||
}
|
||||
```
|
||||
|
||||
3. 🌟🌟
|
||||
```rust,editable
|
||||
|
||||
fn main() {
|
||||
let arr: [i32; 5] = [1, 2, 3, 4, 5];
|
||||
// fill the blanks to make the code work
|
||||
let slice: __ = __;
|
||||
assert_eq!(slice, &[2, 3, 4]);
|
||||
|
||||
println!("Success!")
|
||||
}
|
||||
```
|
||||
|
||||
### string slices
|
||||
4. 🌟
|
||||
```rust,editable
|
||||
|
||||
fn main() {
|
||||
let s = String::from("hello");
|
||||
|
||||
let slice1 = &s[0..2];
|
||||
// fill the blank to make the code work, DON'T USE 0..2 again
|
||||
let slice2 = &s[__];
|
||||
|
||||
assert_eq!(slice1, slice2);
|
||||
|
||||
println!("Success!")
|
||||
}
|
||||
```
|
||||
|
||||
5. 🌟
|
||||
```rust,editable
|
||||
|
||||
fn main() {
|
||||
let s = "你好,世界";
|
||||
// modify this line to make the code work
|
||||
let slice = &s[0..2];
|
||||
|
||||
assert!(slice == "你");
|
||||
|
||||
println!("Success!")
|
||||
}
|
||||
```
|
||||
|
||||
6. 🌟🌟 `&String` can be implicitly converted into `&str`.
|
||||
```rust,editable
|
||||
|
||||
// fix errors
|
||||
fn main() {
|
||||
let mut s = String::from("hello world");
|
||||
|
||||
// here, &s is `&String` type, but `first_word` need a `&str` type.
|
||||
// it works because `&String` can be implicitly converted to `&str, If you want know more ,this is called `Deref`
|
||||
let word = first_word(&s);
|
||||
|
||||
s.clear(); // error!
|
||||
|
||||
println!("the first word is: {}", word);
|
||||
}
|
||||
fn first_word(s: &str) -> &str {
|
||||
&s[..1]
|
||||
}
|
||||
```
|
||||
|
||||
> You can find the solutions [here](https://github.com/sunface/rust-by-practice)(under the solutions path), but only use it when you need it
|
266
en/src/compound-types/string.md
Normal file
266
en/src/compound-types/string.md
Normal file
@ -0,0 +1,266 @@
|
||||
# string
|
||||
The type of string literal `"hello, world"` is `&str`, e.g `let s: &str = "hello, world"`.
|
||||
|
||||
|
||||
### str and &str
|
||||
1. 🌟 We can't use `str` type in normal ways, but we can use `&str`
|
||||
|
||||
```rust,editable
|
||||
|
||||
// fix error without adding new line
|
||||
fn main() {
|
||||
let s: str = "hello, world";
|
||||
|
||||
println!("Success!")
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
2. 🌟🌟 We can only use `str` by boxed it, `&` can be used to convert `Box<str>` to `&str`
|
||||
|
||||
```rust,editable
|
||||
|
||||
// fix the error with at least two solutions
|
||||
fn main() {
|
||||
let s: Box<str> = "hello, world".into();
|
||||
greetings(s)
|
||||
}
|
||||
|
||||
fn greetings(s: &str) {
|
||||
println!("{}",s)
|
||||
}
|
||||
```
|
||||
|
||||
### String
|
||||
`String` type is defined in std and stored as a vector of bytes (Vec<u8>), but guaranteed to always be a valid UTF-8 sequence. String is heap allocated, growable and not null terminated.
|
||||
|
||||
3. 🌟
|
||||
```rust,editable
|
||||
|
||||
// fill the blank
|
||||
fn main() {
|
||||
let mut s = __;
|
||||
s.push_str("hello, world");
|
||||
s.push('!');
|
||||
|
||||
assert_eq!(s, "hello, world!");
|
||||
|
||||
println!("Success!")
|
||||
}
|
||||
```
|
||||
|
||||
4. 🌟🌟🌟
|
||||
```rust,editable
|
||||
|
||||
// fix all errors without adding newline
|
||||
fn main() {
|
||||
let s = String::from("hello");
|
||||
s.push(',');
|
||||
s.push(" world");
|
||||
s += "!".to_string();
|
||||
|
||||
println!("{}", s)
|
||||
}
|
||||
```
|
||||
|
||||
5. 🌟🌟 `replace` can be used to replace substring
|
||||
```rust,editable
|
||||
|
||||
// fill the blank
|
||||
fn main() {
|
||||
let s = String::from("I like dogs");
|
||||
// Allocate new memory and store the modified string there
|
||||
let s1 = s.__("dogs", "cats");
|
||||
|
||||
assert_eq!(s1, "I like cats");
|
||||
|
||||
println!("Success!")
|
||||
}
|
||||
```
|
||||
|
||||
More `String` methods can be found under [String](https://doc.rust-lang.org/std/string/struct.String.html) module.
|
||||
|
||||
6. 🌟🌟 You can only concat a `String` with `&str`, and `String`'s ownership can be moved to another variable
|
||||
|
||||
```rust,editable
|
||||
|
||||
// fix errors without removing any line
|
||||
fn main() {
|
||||
let s1 = String::from("hello,");
|
||||
let s2 = String::from("world!");
|
||||
let s3 = s1 + s2;
|
||||
assert_eq!(s3,"hello,world!");
|
||||
println!("{}",s1);
|
||||
}
|
||||
```
|
||||
|
||||
### &str and String
|
||||
Opsite to the seldom using of `str`, `&str` and `String` are used everywhere!
|
||||
|
||||
7. 🌟🌟 `&str` can be converted to `String` in two ways
|
||||
```rust,editable
|
||||
|
||||
// fix error with at lest two solutions
|
||||
fn main() {
|
||||
let s = "hello, world";
|
||||
greetings(s)
|
||||
}
|
||||
|
||||
fn greetings(s: String) {
|
||||
println!("{}",s)
|
||||
}
|
||||
```
|
||||
|
||||
8. 🌟🌟 We can use `String::from` or `to_string` to convert a `&str` to `String`
|
||||
|
||||
```rust,editable
|
||||
|
||||
// use two approaches to fix the error and without adding a new line
|
||||
fn main() {
|
||||
let s = "hello, world".to_string();
|
||||
let s1: &str = s;
|
||||
|
||||
println!("Success!")
|
||||
}
|
||||
```
|
||||
|
||||
### string escapes
|
||||
9. 🌟
|
||||
```rust,editable
|
||||
fn main() {
|
||||
// You can use escapes to write bytes by their hexadecimal values
|
||||
// fill the blank below to show "I'm writing Rust"
|
||||
let byte_escape = "I'm writing Ru\x73__!";
|
||||
println!("What are you doing\x3F (\\x3F means ?) {}", byte_escape);
|
||||
|
||||
// ...or Unicode code points.
|
||||
let unicode_codepoint = "\u{211D}";
|
||||
let character_name = "\"DOUBLE-STRUCK CAPITAL R\"";
|
||||
|
||||
println!("Unicode character {} (U+211D) is called {}",
|
||||
unicode_codepoint, character_name );
|
||||
|
||||
let long_string = "String literals
|
||||
can span multiple lines.
|
||||
The linebreak and indentation here \
|
||||
can be escaped too!";
|
||||
println!("{}", long_string);
|
||||
}
|
||||
```
|
||||
|
||||
10. 🌟🌟🌟 Sometimes there are just too many characters that need to be escaped or it's just much more convenient to write a string out as-is. This is where raw string literals come into play.
|
||||
|
||||
```rust,editable
|
||||
|
||||
fn main() {
|
||||
let raw_str = r"Escapes don't work here: \x3F \u{211D}";
|
||||
// modify below line to make it work
|
||||
assert_eq!(raw_str, "Escapes don't work here: ? ℝ");
|
||||
|
||||
// If you need quotes in a raw string, add a pair of #s
|
||||
let quotes = r#"And then I said: "There is no escape!""#;
|
||||
println!("{}", quotes);
|
||||
|
||||
// If you need "# in your string, just use more #s in the delimiter.
|
||||
// You can use up to 65535 #s.
|
||||
let delimiter = r###"A string with "# in it. And even "##!"###;
|
||||
println!("{}", delimiter);
|
||||
|
||||
// fill the blank
|
||||
let long_delimiter = __;
|
||||
assert_eq!(long_delimiter, "Hello, \"##\"");
|
||||
|
||||
println!("Success!")
|
||||
}
|
||||
```
|
||||
|
||||
### byte string
|
||||
Want a string that's not UTF-8? (Remember, str and String must be valid UTF-8). Or maybe you want an array of bytes that's mostly text? Byte strings to the rescue!
|
||||
|
||||
**Example**:
|
||||
```rust,editable
|
||||
use std::str;
|
||||
|
||||
fn main() {
|
||||
// Note that this is not actually a `&str`
|
||||
let bytestring: &[u8; 21] = b"this is a byte string";
|
||||
|
||||
// Byte arrays don't have the `Display` trait, so printing them is a bit limited
|
||||
println!("A byte string: {:?}", bytestring);
|
||||
|
||||
// Byte strings can have byte escapes...
|
||||
let escaped = b"\x52\x75\x73\x74 as bytes";
|
||||
// ...but no unicode escapes
|
||||
// let escaped = b"\u{211D} is not allowed";
|
||||
println!("Some escaped bytes: {:?}", escaped);
|
||||
|
||||
|
||||
// Raw byte strings work just like raw strings
|
||||
let raw_bytestring = br"\u{211D} is not escaped here";
|
||||
println!("{:?}", raw_bytestring);
|
||||
|
||||
// Converting a byte array to `str` can fail
|
||||
if let Ok(my_str) = str::from_utf8(raw_bytestring) {
|
||||
println!("And the same as text: '{}'", my_str);
|
||||
}
|
||||
|
||||
let _quotes = br#"You can also use "fancier" formatting, \
|
||||
like with normal raw strings"#;
|
||||
|
||||
// Byte strings don't have to be UTF-8
|
||||
let shift_jis = b"\x82\xe6\x82\xa8\x82\xb1\x82\xbb"; // "ようこそ" in SHIFT-JIS
|
||||
|
||||
// But then they can't always be converted to `str`
|
||||
match str::from_utf8(shift_jis) {
|
||||
Ok(my_str) => println!("Conversion successful: '{}'", my_str),
|
||||
Err(e) => println!("Conversion failed: {:?}", e),
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
A more detailed listing of the ways to write string literals and escape characters is given in the ['Tokens' chapter](https://doc.rust-lang.org/reference/tokens.html) of the Rust Reference.
|
||||
|
||||
### string index
|
||||
11. 🌟🌟🌟 You can't use index to access a char in a string, but you can use slice `&s1[start..end]`.
|
||||
|
||||
```rust,editable
|
||||
|
||||
fn main() {
|
||||
let s1 = String::from("hi,中国");
|
||||
let h = s1[0]; //modify this line to fix the error, tips: `h` only takes 1 byte in UTF8 format
|
||||
assert_eq!(h, "h");
|
||||
|
||||
let h1 = &s1[3..5];//modify this line to fix the error, tips: `中` takes 3 bytes in UTF8 format
|
||||
assert_eq!(h1, "中");
|
||||
|
||||
println!("Success!")
|
||||
}
|
||||
```
|
||||
|
||||
### operate on UTF8 string
|
||||
12. 🌟
|
||||
```rust,editable
|
||||
|
||||
fn main() {
|
||||
// fill the blank to print each char in "你好,世界"
|
||||
for c in "你好,世界".__ {
|
||||
println!("{}", c)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### utf8_slice
|
||||
You can use [utf8_slice](https://docs.rs/utf8_slice/1.0.0/utf8_slice/fn.slice.html) to slice UTF8 string, it can index chars instead of bytes.
|
||||
|
||||
**Example**
|
||||
```rust
|
||||
use utf8_slice;
|
||||
fn main() {
|
||||
let s = "The 🚀 goes to the 🌑!";
|
||||
|
||||
let rocket = utf8_slice::slice(s, 4, 5);
|
||||
// Will equal "🚀"
|
||||
}
|
||||
```
|
||||
|
||||
> You can find the solutions [here](https://github.com/sunface/rust-by-practice)(under the solutions path), but only use it when you need it
|
231
en/src/compound-types/struct.md
Normal file
231
en/src/compound-types/struct.md
Normal file
@ -0,0 +1,231 @@
|
||||
# Struct
|
||||
|
||||
### There types of structs
|
||||
1. 🌟 We must specify concrete values for each of the fields in struct.
|
||||
```rust,editable
|
||||
|
||||
// fix the error
|
||||
struct Person {
|
||||
name: String,
|
||||
age: u8,
|
||||
hobby: String
|
||||
}
|
||||
fn main() {
|
||||
let age = 30;
|
||||
let p = Person {
|
||||
name: String::from("sunface"),
|
||||
age,
|
||||
};
|
||||
|
||||
println!("Success!")
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
2. 🌟 Unit struct don't have any fields. It can be useful when you need to implement a trait on some type but don’t have any data that you want to store in the type itself.
|
||||
```rust,editable
|
||||
|
||||
struct Unit;
|
||||
trait SomeTrait {
|
||||
// ...Some behavours defines here
|
||||
}
|
||||
|
||||
// We don't care the the fields are in Unit, but we care its behaviors.
|
||||
// So we use a struct with no fields and implement some behaviors for it
|
||||
impl SomeTrait for Unit { }
|
||||
fn main() {
|
||||
let u = Unit;
|
||||
do_something_with_unit(u);
|
||||
|
||||
println!("Success!")
|
||||
}
|
||||
|
||||
// fill the blank to make the code work
|
||||
fn do_something_with_unit(u: __) { }
|
||||
```
|
||||
|
||||
3. 🌟🌟🌟 Tuple struct looks similar to tuples, it has added meaning the struct name provides but has no named fields. It's useful when you want give the whole tuple a name, but don't care the fields's names.
|
||||
|
||||
```rust,editable
|
||||
|
||||
// fix the error and fill the blanks
|
||||
struct Color(i32, i32, i32);
|
||||
struct Point(i32, i32, i32);
|
||||
fn main() {
|
||||
let v = Point(__, __, __);
|
||||
check_color(v);
|
||||
|
||||
println!("Success!")
|
||||
}
|
||||
|
||||
fn check_color(p: Color) {
|
||||
let (x, _, _) = p;
|
||||
assert_eq!(x, 0);
|
||||
assert_eq!(p.1, 127);
|
||||
assert_eq!(__, 255);
|
||||
}
|
||||
```
|
||||
|
||||
### Operate on structs
|
||||
|
||||
4. 🌟 You can make a whole struct mutable when instantiate it, but Rust doesn't allow us to mark only certain fields as mutable.
|
||||
|
||||
```rust,editable
|
||||
|
||||
// fill the blank and fix the error without adding/removing new line
|
||||
struct Person {
|
||||
name: String,
|
||||
age: u8,
|
||||
}
|
||||
fn main() {
|
||||
let age = 18;
|
||||
let p = Person {
|
||||
name: String::from("sunface"),
|
||||
age,
|
||||
};
|
||||
|
||||
// how can you believe sunface is only 18?
|
||||
p.age = 30;
|
||||
|
||||
// fill the lank
|
||||
__ = String::from("sunfei");
|
||||
|
||||
println!("Success!")
|
||||
}
|
||||
```
|
||||
|
||||
5. 🌟 Using *field init shorthand syntax* to reduct repetitions.
|
||||
```rust,editable
|
||||
|
||||
// fill the blank
|
||||
struct Person {
|
||||
name: String,
|
||||
age: u8,
|
||||
}
|
||||
fn main() {
|
||||
println!("Success!")
|
||||
}
|
||||
|
||||
fn build_person(name: String, age: u8) -> Person {
|
||||
Person {
|
||||
age,
|
||||
__
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
6. 🌟 You can create instance from other instance with *struct update syntax*
|
||||
```rust,editable
|
||||
|
||||
// fill the blank to make the code work
|
||||
struct User {
|
||||
active: bool,
|
||||
username: String,
|
||||
email: String,
|
||||
sign_in_count: u64,
|
||||
}
|
||||
fn main() {
|
||||
let u1 = User {
|
||||
email: String::from("someone@example.com"),
|
||||
username: String::from("sunface"),
|
||||
active: true,
|
||||
sign_in_count: 1,
|
||||
};
|
||||
|
||||
let u2 = set_email(u1);
|
||||
|
||||
println!("Success!")
|
||||
}
|
||||
|
||||
fn set_email(u: User) -> User {
|
||||
User {
|
||||
email: String::from("contact@im.dev"),
|
||||
__
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Print the structs
|
||||
7. 🌟🌟 We can use `#[derive(Debug)]` to [make a struct prinable](https://doc.rust-lang.org/book/ch05-02-example-structs.html?highlight=%23%5Bderive(Debug)%5D#adding-useful-functionality-with-derived-traits).
|
||||
|
||||
```rust,editable
|
||||
|
||||
// fill the blanks to make the code work
|
||||
#[__]
|
||||
struct Rectangle {
|
||||
width: u32,
|
||||
height: u32,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let scale = 2;
|
||||
let rect1 = Rectangle {
|
||||
width: dbg!(30 * scale), // print debug info to stderr and assign the value of `30 * scale` to `width`
|
||||
height: 50,
|
||||
};
|
||||
|
||||
dbg!(&rect1); // print debug info to stderr
|
||||
|
||||
println!(__, rect1); // print debug info to stdout
|
||||
}
|
||||
```
|
||||
|
||||
### Partial move
|
||||
Within the destructuring of a single variable, both by-move and by-reference pattern bindings can be used at the same time. Doing this will result in a partial move of the variable, which means that parts of the variable will be moved while other parts stay. In such a case, the parent variable cannot be used afterwards as a whole, however the parts that are only referenced (and not moved) can still be used.
|
||||
|
||||
#### Example
|
||||
```rust,editable
|
||||
|
||||
fn main() {
|
||||
#[derive(Debug)]
|
||||
struct Person {
|
||||
name: String,
|
||||
age: Box<u8>,
|
||||
}
|
||||
|
||||
let person = Person {
|
||||
name: String::from("Alice"),
|
||||
age: Box::new(20),
|
||||
};
|
||||
|
||||
// `name` is moved out of person, but `age` is referenced
|
||||
let Person { name, ref age } = person;
|
||||
|
||||
println!("The person's age is {}", age);
|
||||
|
||||
println!("The person's name is {}", name);
|
||||
|
||||
// Error! borrow of partially moved value: `person` partial move occurs
|
||||
//println!("The person struct is {:?}", person);
|
||||
|
||||
// `person` cannot be used but `person.age` can be used as it is not moved
|
||||
println!("The person's age from person struct is {}", person.age);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
#### Exercises
|
||||
|
||||
8. 🌟🌟
|
||||
```rust,editable
|
||||
|
||||
// fix errors to make it work
|
||||
#[derive(Debug)]
|
||||
struct File {
|
||||
name: String,
|
||||
data: String,
|
||||
}
|
||||
fn main() {
|
||||
let f = File {
|
||||
name: String::from("readme.md"),
|
||||
data: "Rust By Practice".to_string()
|
||||
};
|
||||
|
||||
let _name = f.name;
|
||||
|
||||
// ONLY modify this line
|
||||
println!("{}, {}, {:?}",f.name, f.data, f);
|
||||
}
|
||||
```
|
||||
|
||||
> You can find the solutions [here](https://github.com/sunface/rust-by-practice)(under the solutions path), but only use it when you need it
|
89
en/src/compound-types/tuple.md
Normal file
89
en/src/compound-types/tuple.md
Normal file
@ -0,0 +1,89 @@
|
||||
# Tuple
|
||||
1. 🌟 Elements in a tuple can have different types. Tuple's type signature is `(T1, T2, ...)`, where `T1`, `T2` are the types of tuple's members.
|
||||
```rust,editable
|
||||
|
||||
fn main() {
|
||||
let _t0: (u8,i16) = (0, -1);
|
||||
// Tuples can be tuple's members
|
||||
let _t1: (u8, (i16, u32)) = (0, (-1, 1));
|
||||
// fill the blanks to make the code work
|
||||
let t: (u8, __, i64, __, __) = (1u8, 2u16, 3i64, "hello", String::from(", world"));
|
||||
|
||||
println!("Success!")
|
||||
}
|
||||
```
|
||||
|
||||
2. 🌟 Members can be extracted from the tuple using indexing.
|
||||
```rust,editable
|
||||
|
||||
// make it works
|
||||
fn main() {
|
||||
let t = ("i", "am", "sunface");
|
||||
assert_eq!(t.1, "sunface");
|
||||
|
||||
println!("Success!")
|
||||
}
|
||||
```
|
||||
|
||||
3. 🌟 Long tuples cannot be printed
|
||||
```rust,editable
|
||||
|
||||
// fix the error
|
||||
fn main() {
|
||||
let too_long_tuple = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13);
|
||||
println!("too long tuple: {:?}", too_long_tuple);
|
||||
}
|
||||
```
|
||||
|
||||
4. 🌟 Destructuring tuple with pattern.
|
||||
```rust,editable
|
||||
|
||||
fn main() {
|
||||
let tup = (1, 6.4, "hello");
|
||||
|
||||
// fill the blank to make the code work
|
||||
let __ = tup;
|
||||
|
||||
assert_eq!(x, 1);
|
||||
assert_eq!(y, "hello");
|
||||
assert_eq!(z, 6.4);
|
||||
|
||||
println!("Success!")
|
||||
}
|
||||
```
|
||||
|
||||
5. 🌟🌟 Destructure assignments.
|
||||
```rust,editable
|
||||
fn main() {
|
||||
let (x, y, z);
|
||||
|
||||
// fill the blank
|
||||
__ = (1, 2, 3);
|
||||
|
||||
assert_eq!(x, 3);
|
||||
assert_eq!(y, 1);
|
||||
assert_eq!(z, 2);
|
||||
|
||||
println!("Success!")
|
||||
}
|
||||
```
|
||||
|
||||
6. 🌟🌟 Tuples can be used as function arguments and return values
|
||||
```rust,editable
|
||||
|
||||
fn main() {
|
||||
// fill the blank, need a few computations here.
|
||||
let (x, y) = sum_multiply(__);
|
||||
|
||||
assert_eq!(x, 5);
|
||||
assert_eq!(y, 6);
|
||||
|
||||
println!("Success!")
|
||||
}
|
||||
|
||||
fn sum_multiply(nums: (i32, i32)) -> (i32, i32) {
|
||||
(nums.0 + nums.1, nums.0 * nums.1)
|
||||
}
|
||||
```
|
||||
|
||||
> You can find the solutions [here](https://github.com/sunface/rust-by-practice)(under the solutions path), but only use it when you need it
|
Reference in New Issue
Block a user