📔 I used the comprehensive-rust by Google as learning resource. It is used for internal educating at first but recently released to general public. If you want to learn Rust, check out this great resource!
❗This is the 2nd learning note of this series. If you want to learn from start and it's your first time here, make sure to check out the 1st post.
Alright, let's get started.
Definition: A block in Rust contains a bunch of expressions. They are wrapped by {}
. The block actually has a value and type, which are defined by the last expression of the block.
Note that, sometimes, you cannot write return
in the block. See the wrong example $1.2$ below, it will throw compile error. I guess the compiler will seem the return
inside the block as the return
for the main
function. And the error message will tell you the expression under the first return
is unreachable.
But if there are no such conflict, you can feel free to use both. (example $2.1$ & $2.2$)
It basically works like other language.
But there is a cool expression like this
I think it's like ternary operator in Javascript and similar thing in Python
❗Note that, in Rust, two branch blocks of the if
expression must have the same type.
They are both similar to Python.
range(start, end)
can be written in Rust like this (start..end)
(start..end).step_by(step_size)
break
and continue
are the same as other languages. But there is a cool thing like this. You can label break
and continue
, and while
loop to choose which loop you want to break
or continue
when there are nested while
loops.
In this case, we break the outer loop after 3 iterations of the inner loop.
The output world be
x: 10
x: 10, i: 0
x: 10, i: 1
x: 10, i: 2
It's an infinite loop. You can think it as while true
. The only way to stop is use break
or return
.
const
defines a value which cannot be changed after declaration. To comply convention, we use ALL_UPPER_CASE_AND_UNDERSCORE
to name the variable.
In above code snippet:
Option<T>
is a Rust enum that represents an optional value: either a value of type T
(need to wrap the value in Some
) exists or does not exist(None
). It's similar to T?
in Typescript.
unwrap_or(value)
is a method to unwrap Option<T>
(produced by Some()
), if the value is not None
, unwrap_or
will return its value, else if it's None
, it will return value
.string.as_bytes()
converts string into a byte slice. And then iter()
creates an iterator allows us to loop over it. enumerate()
is identical to Python's enumerate
, which let you to access index of the slice.wrapping_add
is to prevent overflow at runtime, which can cause panic. After adding this, it will provide function like modulo operation. So, let's say we have an u8
equal to its max value 255. After adding 1 by wrappin_add(1)
, it will become 0 rather than overflow.
`Static variables will live during the whole execution of the program, and therefore will not move. It's similar to const
but different in some ways.
const
is compile time constant; static
is runtime constant.const
will be copy to each line that use that const
at compile time. Therefore, const
don't have a fixed memory location.static
is still there at runtime. It has fixed memory location.It's different from mutation because when shadowing, both variable's (with same name) memory locations exist at the same time.
An enum itself is a type(like WebEvent
below). An enum can have several Variants. Variant can have payload (like KeyPress
and Click
below). Note that we need to write namespace to access the Variant, like WebEvent::PageLoad
.
We can use match
(like switch
and case
in other languages) to do pattern matching. Note that there is no fall-through like other languages, so we don't need to add break
for each case.
It let you execute different blocks depending on whether a value matches a pattern.
In the above code, Some(value)
can either be value
or None
depending on the arg
.
Similar to if let
, if the while let
failed, the loop will be break. In the example, iter
has type Option<i32>
.
Pattern matching match
expression can not only destruct Enums(e.g. the WebEvent
example above), it can also destruct structs
.
In this example, the first condition matches struct
Foo
when x.0 == 1
. The second one catches when y==2
. And the last expression is the wildcard, and it only need to use y
's value.
Note that, in this example, even y==2
, since the first line matches first. It will print x.0 = 1, b = 2, y = 2
instead of y = 2, x = (1, 2)
. So, order matters.
Of course, you can also destruct arrays or tuple. The code looks very similar as above example and self-explainable.
When pattern matching, you can also write some expression to achieve some custom logics. This is called Match Guards. It's a flexible way to do pattern matching. The arm will be triggered only when the boolean expression return true.
The Luhn algorithm is used to validate credit card numbers. The algorithm takes a string as input and does the following to validate the credit card number:
Ignore all spaces. Reject number with less than two digits.
Moving from right to left, double every second digit: for the number 1234
, we double 3
and 1
. For the number 98765
, we double 6
and 8
.
After doubling a digit, sum the digits if the result is greater than 9. So doubling 7
becomes 14
which becomes 1 + 4 = 5
.
Sum all the undoubled and doubled digits.
The credit card number is valid if the sum ends with 0
.
My solution:
|c| c.is_alphabetic()
is how Rust write Lambda function like Python.u8
and collect by Vec
.bool
.