Pandy's Blog

Pandy Song

Rust lang features highlight

Introduction

Highlight the features that Rust has comparing to C/C++, which really reduce the pain when using C/C++. This could remind me of highlighted features.

variable shadow and immutable default

These features are for high performance and parallelization.

expression and statement

Expression make the inner function possible and much easier.

labeled loop

Much more clear to break the outer loop.

loop expression with break to return a value

all pythonic way to loop over

fn main() {
    let a = [10, 20, 30, 40, 50];

    for element in a {
        println!("the value is: {}", element);
    }
}

pythonic types: tuple and array

Ownership: make it stand out from other language

Ownership make it possible to allocate some object in some init() function and transfer the Ownership to another function and free the memory there. The design pattern is very useful. If it would be done in C++, it would be very cumbersome.

“move” feature, s1 is moved to s2

Rust considers s1 as no longer valid. Therefore, Rust doesn’t need to free anything when s1 goes out of scope:

    let s1 = String::from("hello");
    let s2 = s1;

    println!("{}, world!", s1);

Stack-Only Data: Copy

    let x = 5;
    let y = x;

    println!("x = {}, y = {}", x, y);

“copy” trait vs “clone” trait


So what types implement the Copy trait? You can check the documentation for the
given type to be sure, but as a general rule, any group of simple scalar values
can implement Copy, and nothing that requires allocation or is some form of
resource can implement Copy. Here are some of the types that implement Copy:

- All the integer types, such as u32.
- The Boolean type, bool, with values true and false.
- All the floating point types, such as f64.
- The character type, char.
- Tuples, if they only contain types that also implement Copy. For example,
  (i32, i32) implements Copy, but (i32, String) does not.

‘only one mutable reference’


Mutable references have one big restriction: you can have only one mutable
reference to a particular piece of data at a time.

This is For PERFORMANCE?

The benefit of having this restriction is that Rust can prevent data races at
compile time. A data race is similar to a race condition and happens when these
three behaviors occur:

- Two or more pointers access the same data at the same time.
- At least one of the pointers is being used to write to the data.
- There’s no mechanism being used to synchronize access to the data.

NLL (Non-Lexical Lifetimes)


The ability of the compiler to tell that a reference is no longer being used at
a point before the end of the scope is called Non-Lexical Lifetimes (NLL for
short), and you can read more about it in The Edition Guide.

Dangling References

In languages with pointers, it’s easy to erroneously create a dangling pointer–a pointer that references a location in memory that may have been given to someone else–by freeing some memory while preserving a pointer to that memory. In Rust, by contrast, the compiler guarantees that references will never be dangling references: if you have a reference to some data, the compiler will ensure that the data will not go out of scope before the reference to the data does.

The Rules of References

Let’s recap what we’ve discussed about references:

  • At any given time, you can have either one mutable reference or any number of immutable references.
  • References must always be valid.

slicing is similar to python

String Slices as Parameters

Knowing that you can take slices of literals and String values leads us to one more improvement on first_word, and that’s its signature:

fn first_word(s: &String) -> &str {

A more experienced Rustacean would write the signature shown in Listing 4-9 instead because it allows us to use the same function on both &String values and &str values.

fn first_word(s: &str) -> &str {

Using the Field Init Shorthand


fn build_user(email: String, username: String) -> User {
    User {
        email,
        username,
        active: true,
        sign_in_count: 1,
    }
}

tuple structs is like NamedTuple in Python

Unit-Like Structs Without Any Fields


struct AlwaysEqual;

fn main() {
    let subject = AlwaysEqual;
}

using ‘dbg!’ to print values when debugging

very convenient. Rust takes most of the common tasks out of box.

Associated Functions

Decouple the data structure of an object with’s associated functions. This reflects the fundamental principle of software engineering: “decouple if it could be”.

null

In his 2009 presentation “Null References: The Billion Dollar Mistake,” Tony Hoare, the inventor of null, has this to say:


I call it my billion-dollar mistake. At that time, I was designing the first
comprehensive type system for references in an object-oriented language. My
goal was to ensure that all use of references should be absolutely safe, with
checking performed automatically by the compiler. But I couldn’t resist the
temptation to put in a null reference, simply because it was so easy to
implement. This has led to innumerable errors, vulnerabilities, and system
crashes, which have probably caused a billion dollars of pain and damage in the
last forty years.

enum option

If the type is Option, it is possible that the value is Null, so you have to write code to handle the Null case:


Eliminating the risk of incorrectly assuming a not-null value helps you to be
more confident in your code. In order to have a value that can possibly be null,
you must explicitly opt in by making the type of that value Option<T>. Then,
when you use that value, you are required to explicitly handle the case when
the value is null. Everywhere that a value has a type that isn’t an Option<T>,
you can safely assume that the value isn’t null. This was a deliberate design
decision for Rust to limit null’s pervasiveness and increase the safety of Rust
code.

The way it handles option value provides a lot of robust.

match vs ‘if let’

use … as to resolve the name conflicting

Re-exporting Names with pub use

Option, Result and ‘?’ operator

Need to review the book section:

https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html

unwrap and expect


Similarly, the unwrap and expect methods are very handy when prototyping,
before you’re ready to decide how to handle errors. They leave clear markers in
your code for when you’re ready to make your program more robust.

Traits: Defining Shared Behavior

traits are a like duck-typing where it was only possible in python, but it is implemented in Rust.

Specifying Multiple Trait Bounds with the + Syntax


pub fn notify(item: &(impl Summary + Display)) {

Clearer Trait Bounds with where Clauses


fn some_function<T, U>(t: &T, u: &U) -> i32
    where T: Display + Clone,
          U: Clone + Debug
{

Validating References with Lifetimes

need to re-read again and again:

https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html

#[should_panic] annotion


#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    #[should_panic]
    fn greater_than_100() {
        Guess::new(200);
    }
}

Deref Coersion

https://doc.rust-lang.org/book/ch15-02-deref.html

borrow_mut() and borrow() in RefCell

It give a flexibility for compiler to escape to check. Instead, it becomes run-time checking.