24 days from Node.js to Rust

preface

The lifecycle concept of Rust is focused on avoiding empty references, which can cause an application to crash for users and can be a vulnerability for malicious attackers.

Note: Empty references are the source of most software errors. According to the Chromium team, 70% of serious accidents are memory-safe (see article), so if you’re interested, read this article about browser memory safety

The memory security issue is a bit unfamiliar to developers who have never used a language like C for memory management. JavaScript and Java use garbage collection to manage your memory and avoid empty references. The garbage collector tracks your references to data until the number of references to data is zero

Just 63 years ago, garbage collection was very, very revolutionary, but it came at a cost. In extreme cases, garbage collection made programs unresponsive for a few seconds

Rust does not use garbage collection to avoid empty references, and you can get the efficiency of C with the security of JavaScript

Note: There are many articles on the Rust lifecycle online. This tutorial will not cover everything, but just some of the confusing questions. The subsequent readings are a prerequisite for reading this article

The body of the

Lifecycle & Lifecycle annotations

You’ll often see “lifecycle annotations” shortened to “lifecycle” in articles, but this is just confusing

  • Life cycle:RustBorrow inspector(borrow checkerEvery variable has a point in time when it is created and destroyed, which is its life cycle
  • Life cycle Notes: One kindRustDevelopers can add a named tag to a reference to give it a lifetime, which makes sense when the scenario is multiple references

When you read “you must specify the life cycle” or “give a reference a life cycle”, the “life cycle” here is really “life cycle annotation”.

Lifecycle annotations are omitted

Every reference has a life cycle, regardless of whether there are annotations or not, and sometimes not annotating does not mean avoiding the life cycle

For example, this function:

fn omits_annotations(list: &[String]) -> OptionThe < &String> {
  list.get(0)}Copy the code

Is equivalent to the following line number:

fn has_annotations<'a>(list: &'a [String]) -> OptionThe < &'a String> {
  list.get(1)}Copy the code

Use is also consistent:

fn main() {
  let authors = vec!["Samuel Clemens".to_owned(), "Jane Austen".to_owned()];
  let value = omits_annotations(&authors).unwrap();
  println!("The first author is '{}'", value);
  let value = has_annotations(&authors).unwrap();
  println!("The second author is '{}'", value);
}
Copy the code

Output result:

The first author is 'Samuel Clemens'
The second author is 'Jane Austen'
Copy the code

The annotation can be omitted because there is only one input parameter and one return value, and Rust automatically concludes that their lifecycles are the same

Static declaration cycle (‘static)

The Rust community often refers to static, and there are many explanations for static, but this makes it more complicated, so let’s make it as brief as possible

There are two classic use scenarios for ‘static ‘:

(I) Explicit lifetime comments as references

fn main() {
  let mark_twain = "Samuel Clemens";
  print_author(mark_twain);
}
fn print_author(author: &'static str) {
  println!("{}", author);
}
Copy the code

(2) life cycle limits as generic parameters

fn print<T: Display + 'static>(message: &T) {
  println!("{}", message);
}
Copy the code

The presence of static means that the reference is valid for the rest of the program, and the data to which it points cannot be moved or changed. String literals are &’static,

Static does not apply to a variable that holds a reference. The get_memory_location() function returns a pointer and length to a string literal, and the get_str_at_location() function takes a pointer and length and reads the contents of the specified location in memory

use std::{slice::from_raw_parts, str::from_utf8_unchecked};

fn get_memory_location() - > (usize.usize) {
    let string = "Hello World!";
    let pointer = string.as_ptr() as usize;
    let length = string.len();
    (pointer, length)
    // `string` is dropped here.
    // It's no longer accessible, but the data lives on.
}

fn get_str_at_location(pointer: usize, length: usize) - > &'static str {
    // Notice the `unsafe {}` block. We can't do things like this without
    // acknowledging to Rust that we know this is dangerous.
    unsafe { from_utf8_unchecked(from_raw_parts(pointer as *const u8, length)) }
}

fn main() {
    let (pointer, length) = get_memory_location();
    let message = get_str_at_location(pointer, length);
    println!(
        "The {} bytes at 0x{:X} stored: {}",
        length, pointer, message
    );
    // If you want to see why dealing with raw pointers is dangerous,
    // uncomment this line.
    // let message = get_str_at_location(1000, 10);
}
Copy the code

The output is:

The 12 bytes at 0x562037200057 stored: Hello World!
Copy the code

On the other hand, adding ‘static ‘as a constraint tells Rust that you want the type to persist, not the data

You need to understand that &’static ‘does not equal T: ‘static

The following code confirms how the String in the second block satisfies the static_bound() ‘static constraint without retaining the same properties of &’static in the first block

use std::fmt::Display;

fn main() {
    let r1;
    let r2;
    {
        static STATIC_EXAMPLE: i32 = 42;
        r1 = &STATIC_EXAMPLE;
        let x = "&'static str";
        r2 = x;
    }
    println!("&'static i32: {}", r1);
    println!("&'static str: {}", r2);
    let r3;
    {
        let string = "String".to_owned();

        static_bound(&string); // This is *not* an error
        r3 = &string; // *This* is
    }
    println!("{}", r3);
}

fn static_bound<T: Display + 'static>(t: &T) {
    println!("{}", t);
}
Copy the code

Results:

error[E0597]: `string` does not live long enough
  --> crates/day-16/static/src/main.rs:21:10
   |
21 |     r3 = &string;
   |          ^^^^^^^ borrowed value does not live long enough
22 |   }
   |   - `string` dropped here whilestill borrowed 23 | println! ("{}", r3);
   |                  -- borrow later used here

For more information about this error, try `rustc --explain E0597`.
Copy the code

Although the two uses are related, the spirit behind them is different

If you want to solve the problem by adding &’static, then you need to rethink and see if it’s the right thing to do. If you need to add ‘static to solve the problem, that’s usually fine

reading

  • The Rust Book: ch 10.03 – Validating References with Lifetimes
  • Rust by Example: Lifetimes
  • Rust Reference: Trait and lifetime bounds
  • Rust Reference: Lifetime elision
  • Rustonomicon: Lifetimes
  • Common Rust Lifetime Misconceptions
  • rustviz: Rust lifetime visualizer
  • Understanding lifetimes in Rust

conclusion

After five iterations of this article, the biggest headaches are generics, life cycles, best practices in reference to third-party libraries, and asynchronous code, which we haven’t covered yet.

We will cover how to use Rust to improve the performance of JavaScript applications, how to build a Web server, how to read and write files, and how to serialize and deserialize JSON