Node.js 开发者的 Rust 入门指南

更新日期: 2022-01-28阅读: 937标签: node

随着WebAssembly的进步,如果你想在JavaScript和Node.js的基础上,提高浏览器、服务器和边缘计算的性能,那么可以了解一下Rust。

Node.js技术栈与Rust的结合简直是天作之合,因为Rust能提供WebAssembly支持,而WebAssembly能在Node.js上运行。

本文将详细地介绍如何在Node.js上编译Rust,并运行WebAssembly。

注意:对于以JavaScript为主的Node.js开发者来说,你可能不太熟悉类似于“std::wx::y”或“&xyz”之类的表述,但是没关系,我会详细解释。

与JavaScript和Node.js相比,Rust是一门较为低级的语言。这意味着,你需要熟悉计算机的工作原理,才能真正理解Rust。而Node.js更为高级,通常接触不到这些表述。

别忘了,Rust最初是一门非常接近底层硬件的系统编程语言。这样能获得更高的性能,但也会导致更高的复杂性。

Rust不会隐藏变量位于栈上还是堆上、以及因此导致的限制等细节。但它也提供了大量的库和模块(在Rust中称为crate),这一点很像Node.js,因此编程难度并不高。

1、创建一个Rust项目

本文所有的代码都可以利用 Rust playground 在线运行(当然,除了那些需要访问本地的代码之外):https://play.rust-lang.org/。

在安装好Rust之后,利用cargo命令(Rust的包管理器)创建一个新项目:

cargo new <PROJECT_NAME>

这个命令将在当前目录下创建一个新文件夹。

或者你也可以将当前目录作为项目文件夹:

cargo init

源代码位于 src/ 目录下,入口为main.rs文件中的main函数(用fn关键字定义)。

fn main() {
  println!("Hello, world!");
}

2、输出

Rust使用“宏”来输出到控制台。Rust中的宏用感叹号(!)表示。println! 宏非常灵活:

fn main() {
    // string interpolation
    println!("Adding {} and {} gives {}", 22, 33, 22 + 33);
    // positional arguments
    println!(
        "Ypur name is {0}. Welcome to {1}. Nice to meet you {0}",
        "Goto", "Rust"
    );
    // named arguments
    println!(
        "{language} is very popular. It was created in {year}",
        language = "Rust",
        year = 2010
    );
    // placeholder traits (using positional argument to avoid repeat)
    println!("{0}, in binary: {0:b}, in hexadecimal: {0:x}", 11);
    // debug trait (very useful to print anything)
    // if you try to print the array directly, you will get an error
    // because an array is not a string or number type
    println!("{:?}", [11, 22, 33]);
}

运行代码查看输出:

cargo run

你将会看到下面的结果(以及一大堆编译信息——Rust是一门编译语言):

Adding 22 and 33 gives 55
Ypur name is Goto. Welcome to Rust. Nice to meet you Goto
Rust is very popular. It was created in 2010
Decimal: 11      Binary: 1011    Hexadecimal: b
[11, 22, 33]

在Rust中,行尾必须使用分号(;),除非是函数最后一行的返回语句(稍后进一步解释)。

3、对数值输出进行高级格式化

fn main() {
    let x = 246.92385;
    let y = 24.69;
    let z = x / y;

    // print line macro with 3 decimal point precision

    println!("z is {:.3}", z);

    // 9: total character space the number to occupy
    // (adds pre padding if necessary)

    println!("z is {:9.3}", z);

    // 0: placeholder number for padding characters

    println!("z is {:09.3}", z);
    println!("z is {:09.3}\nx is {}", z, x);

    // print macro without new line

    print!("y is {:09.3}\n x is {}\n", y, x);
    // positional parameters

    println!("z is {0:05.1} and x is {1:.2}. \nx is also {1}", z, x)
}

运行后输出:

z is 10.001
z is    10.001
z is 00010.001
z is 00010.001
x is 246.92385
y is 00024.690
 x is 246.92385
z is 010.0 and x is 246.92. 
x is also 246.92385

4、变量

fn main() {
    // variables are immutable by default
    // stored on the heap (more on that later)
    let pc = "Inspirion XYZ";
    println!("pc is {}", pc);
    // mutable variables
    let mut age = 1;
    println!("age is {}", age);
    age = 2;
    println!("age is {}", age);
    // constants (must be uppercase and explicit type definition)
    const BRAND: &str = "Dell";
    println!("brand is {}", BRAND);
    // multiple assignment (tuple destructuring)
    // more on tuples later in the article
    let (status, code) = ("OK", 200);
    println!("status: {}, code: {}", status, code);
}

输出结果:

pc is Inspirion XYZ
age is 1
age is 2
brand is Dell
status: OK, code: 200

5、基本类型

fn main() {
    // default integer numeric type is i32
    let num1 = 123;
    println!("{} - type: {}", num1, get_type(&num1));
    // default floating point numeric type is f64
    let num2 = 1.23;
    println!("{} - type: {}", num2, get_type(&num2));
    // explicit typing
    let num3: i8 = 23;
    println!("{} - type: {}", num3, get_type(&num3));
    // max values
    // std is the standard library/crate, 
    // it gives access to a rich variety of features, 
    // here we use the type modules (i32, i16, etc.) and properties
    let max_i32 = i32::MAX;
    let max_i16 = i16::MAX;
    println!("max value for i32 is {}", max_i32);
    println!("max value for i16 is {}", max_i16);
    // boolean
    let is_rust_fun: bool = true;
    println!(
        "is_rust_fun is {} - type: {}",
        is_rust_fun,
        get_type(&is_rust_fun)
    );
    let is_greater = 23 > 5;
    println!(
        "is_greater is {} - type: {}",
        is_greater,
        get_type(&is_greater)
    );
    // characters (unicode - up to 4 bytes length)
    let smiley = ':smiling_imp:';
    println!("smiley is {} - type: {}", smiley, get_type(&smiley));
}
// helper function to print types
fn get_type<T>(_: &T) -> &str {
    std::any::type_name::<T>()
}

输出结果:

123 - type: i32
1.23 - type: f64
23 - type: i8
max value for i32 is 2147483647
max value for i16 is 32767
is_rust_fun is true - type: bool
is_greater is true - type: bool
smiley is :smiling_imp: - type: char

6、浮点数

f32 (32位浮点数)

f64 (64位浮点数)

fn main() {
    // by default fractional values stored in f64
    
    let my_float = 12.345677890123456789012345;
    println!("my_float is: {}", my_float);
    let a_float: f32 = 9.9438535983578493758;
    println!("a_float is: {}", a_float);
    let min_f32 = f32::MIN;
    println!("min_f32 is: {}\n", min_f32);
    let max_f32 = f32::MAX;
    println!("max_f32 is: {}\n", max_f32);
    let min_f64 = f64::MIN;
    println!("min_f64 is: {}\n", min_f64);
    let max_f64 = f64::MAX;
    println!("max_f64 is: {}\n", max_f64);
}

输出结果:

my_float is: 12.345677890123456
a_float is: 9.943853
min_f32 is: -340282350000000000000000000000000000000
max_f32 is: 340282350000000000000000000000000000000
min_f64 is: -179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
max_f64 is: 179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

7、位操作(高级内容,可以跳过)

/*
Bitwise operations: on individual bits rather than sets of bytes.
- binary representation, a sequence of bytes
- underscore separator allowed for legibility
- by default binary representations are store as i32
*/
fn main() {
    // stored as u8 by adding suffix u8
    
    let mut value = 0b1111_0101u8;
    // will print base 10 (decimal) representation
    
    println!("value is {}", value);
    /*
    :08b
        0 -> display leading zeros
        8 -> number of bits to display
        b -> display binary representation
    */
    println!("value is {:08b}", value);
    // bitwise NOT: invert individual bits
    
    value = !value; // 0000_1010
    println!("value is {:08b}", value);
    // bitwise AND: used to clear the value of a specific bit
    value = value & 0b1111_0111; // -> 0000_0010
    println!("value is {:08b}", value);
    // bitwise AND: used to check value of a specific bit
    // if a specific bit is 0 or 1, useful to check status of registers for process state
    
    println!("value is {:08b}", value & 0b0100_0000); 
    // -> 0000_0000
    // bitwise OR: if either operand is 1, result is 1
    // useful to set value of a specific bit
    
    value = value | 0b0100_0000; // -> 0100_0010
    println!("value is {:08b}", value);
   // bitwise XOR (exclusive OR): 
   // result is 1 only when bits are different, otherwise 0
   // useful to set if bits are different
    
   value = value ^ 0b0101_0101; // -> 0001_0111
   println!("value is {:08b}", value);
   ////////////////////////////
   // Bit Shift operators
   ////////////////////////////
   // shift bit pattern left or right by a number of bits
   // and backfill shifted bit spaces with zeros
   // shift left by 4 bits
    
   value = value << 4; // -> 0111_0000
   println!("value is {:08b}", value);
   // shift right by 3 bits
    
   value = value >> 3; // -> 0000_1110
   println!("value is {:08b}", value);
}

输出结果:

value is 245
value is 11110101
value is 00001010
value is 00000010
value is 00000000
value is 01000010
value is 00010111
value is 01110000
value is 00001110

8、布尔和二进制代数

fn main() {
    let a = true;
    let b = false;
    println!("a is {}\nb is {}", a, b);
    println!("NOT a is {}", !a);
    println!("a AND b is {}", a & b);
    println!("a OR b is {}", a | b);
    println!("a XOR b is {}", a ^ b);
    // boolean casted to integer begets 0 or 1
    println!("a XOR b is {}", (a ^ b) as i32); // 1
   let c = (a ^ b) | (a & b);
    println!("c is {}", c);
    // short-circuiting logical operations:
    // right operand not evaluated
    let d = true || (a & b);
    println!("d is {}", d);
    // the panic macro is not evaluated, 
    // so the process ends with status 0 (OK, no error)
    // panics exit the program immediately (like throwing error in Node.js)
    let e = false && panic!();
    println!("e is {}", e);
}

输出结果:

a is true
b is false
NOT a is false
a AND b is false
a OR b is true
a XOR b is true
a XOR b is 1
c is true
d is true
e is false

9、算术操作

fn main() {
    // can only do arithmetic operations on same type operands
    let a = 11;
    let b = 33;
    let c = a + b;
    println!("c is {}", c);
    let d = c - b;
    println!("d is {}", d);
    let e = a * d;
    println!("e is {}", e);
    // type casting (careful with precision loss and type compatibility)
    let f = c as f32 / 4.5;
    println!("f is {}", f);
    // operator precedence control
    
    let g = 43.5432 % (a as f64 * e as f64);
    println!("g is {}", g);
}

输出结果:

c is 44
d is 11
e is 121
f is 9.777778
g is 43.5432

10、比较操作

/*
can only compare values of same type
*/
fn main() {
    let a = 11;
    let b = 88;
    println!("a is {}\nb is {}", a, b);
    println!("a EQUAL TO b is {}", a == b);
    println!("a NOT EQUAL TO b is {}", a != b);
    println!("a GREATER THAN b is {}", a > b);
    println!("a GREATER THAN OR EQUAL TO b is {}", a >= b);
    println!("a LESS THAN b is {}", a < b);
    println!("a LESS THAN OR EQUAL TO b is {}", a <= b);
    let c = true;
    let d = false;
    println!("\nc is {}\nd is {}", c, d);
    println!("c EQUAL TO d is {}", c == d);
    println!("c NOT EQUAL TO d is {}", c != d);
    println!("c GREATER THAN d is {}", c > d);
    println!("c GREATER THAN OR EQUAL TO d is {}", c >= d);
    println!("c LESS THAN d is {}", c < d);
    println!("c LESS THAN OR EQUAL TO d is {}", c <= d);
}

输出结果:

a is 11
b is 88
a EQUAL TO b is false
a NOT EQUAL TO b is true
a GREATER THAN b is false
a GREATER THAN OR EQUAL TO b is false
a LESS THAN b is true
a LESS THAN OR EQUAL TO b is true
c is true
d is false
c EQUAL TO d is false
c NOT EQUAL TO d is true
c GREATER THAN d is true
c GREATER THAN OR EQUAL TO d is true
c LESS THAN d is false
c LESS THAN OR EQUAL TO d is false

11、字符

fn main() {
    // Unicode scalar value stored using 4 bytes (32 bits)
    // contrary to C like languages that store it in 1 byte
    let letter: char = 'z';
    let number_char = '9';
    let finger = '\u{261D}';
    println!("letter is {}", letter);
    println!("number_char is {}", number_char);
    println!("finger is {}", finger);
}

输出结果:

letter is z
number_char is 9
finger is ☝

12、计算平均值

fn main() {
    let a = 33;
    let b = 4.9;
    let c: f32 = 123.5;
    let average = (a as f32 + b as f32 + c) / 3.0;
    println!("average is {}", average);
    assert_eq!(average, 53.8);
    println!("test passed.");
}

输出结果:

average is 53.8
test passed.

13、数组

fn main() {
    // fixed length and single typed
    // stored in contiguous memory locations
    // Contiguous means that elements are laid out so that every element is the same distance from its neighbors.
    let letters = ['a', 'b', 'c']; // type: [char; 3]
    let first_letter = letters[0];
    println!("first_letter is {}", first_letter);
    // to modify elements in array, it must be mutable
    let mut numbers = [11, 22, 44]; // type is [i32; 3]
    numbers[2] = 33;
    println!("numbers is {}", numbers[2]);
    // empty array declaration (memory allocated)
    let words: [&str; 2];
    words = ["ok"; 2]; // repeat expression, equivalent to ["ok", "ok"]
    println!("words is {:?}", words);
    /*
    length of usize is based on number of bytes needed to reference memory in your target architecture:
    - for 32 bit compilation target -> usize is 4 bytes
    - for 64 bit compilation target -> usize is 8 bytes
    */
    let ints = [22; 5];
    let length: usize = ints.len();
    println!("length is {}", length);
    // get size in memory (mem module of the std crate)
    let mem_size_byte = std::mem::size_of_val(&ints);
    println!("mem_size_byte is {}", mem_size_byte);
    // get slice from array
    let mut slice: &[i32] = &ints;
    println!("slice is {:?}", slice);
    
    slice = &ints[3..5];
    println!("slice is {:?}", slice);
}

输出结果:

first_letter is a
numbers is 33
words is ["ok", "ok"]
length is 5
mem_size_byte is 20
slice is [22, 22, 22, 22, 22]
slice is [22, 22]

14、slice

fn main() {
    // slice: used to reference a contiguous section (subset) of a collection (aarray, etc.) WITHOUT taking ownership of these elements
    // commonly used: string slice dt type : &str
    // string literals are slices
    // the data is hard-coded in the executable binary and the program  uses a string slice to access it
   // create a string slice from string data
    // the sentence variable has a pointer to the beginning of the string data in memory as well, as info about its length and capacity
    let sentence = String::from("This is a sequence of words.");
    println!("sentence is {}", sentence);
    // the last_word variable has a pointer to the offset/start index of the section of the string data, as well as the length of the slice
    // as usual, the end index of the slice is excluded from the result (common in most programming languages when slicing collections)
    let last_word = &sentence[22..22 + 5];          // [start..end_excluded]
    println!("last_word is \"{}\"", last_word);
    // slice from offset index to end of the collection (here string data)
    let last_part: &str = &sentence[22..];
    println!("last_part is \"{}\"", last_part);
    // slice from beginning of collection up until end index
    let without_last_word = &sentence[..22];
    println!("without_last_word is \"{}\"", without_last_word);
    // the length of a string slice is in number of bytes (usize data type)
    // NOT in number of characters
    let slice_length: usize = last_part.len();
    println!("slice_length is {} bytes", slice_length);
    // when creating a string slice, the range indices must occur at valid UTF-8 character boundaries
    // remember that UTF-8 characters can occupy multiple bytes
    // all this to say that slice range indices must be character boundaries
    // if you index in the middle of a character, the program will panic
    // so be careful when creating string slices from strings with special characters or emojis
   // yes, this is some low-level stuff that we usually don't deal with in everyday Node.js
}

输出结果:

sentence is This is a sequence of words.
last_word is "words"
last_part is "words."
without_last_word is "This is a sequence of "
slice_length is 6 bytes

15、slice 作为参数

fn main() {
    let message = String::from("lorem ipsum");
    let first_word = get_first_word(&message);
    println!("first_word is \"{}\"", first_word);
    let first_word_too = get_first_word_too(&message[6..]);
    println!("first_word_too is \"{}\"", first_word_too);
}
// ======= passing the entire string as input ===========
fn get_first_word(msg: &String) -> &str {
    // create a slice of bytes (&[u8] data type) from string data
    let bytes: &[u8] = msg.as_bytes();
    // iterate through byte sequence one byte at a time
    // use enumerate() to get the index when iterating
   for (index, &item) in bytes.iter().enumerate() {
        // find first space and return everything before as a string slice
        // b' ' is the byte representation of a blank space
        // remember that we are iterating on a sequence of bytes, NOT characters
       // we do that because the index for a string slice is in terms of bytes
       if item == b' ' {
            return &msg[..index];
       }
    }
    // no blank space found, return entire message
    &msg
}
// ======= passing a string slice as input ===========
fn get_first_word_too(msg: &str) -> &str {
    let bytes: &[u8] = msg.as_bytes();
    for (index, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &msg[..index];
        }
    }
    &msg
}

输出:

first_word is "lorem"
first_word_too is "ipsum"

关于 Deref coercion:

传递对字符串的借用引用 (&String)与传递字符串切片 (&str) 不同

借用字符串引用指向堆栈上的字符串,该字符串又拥有并指向堆上的数据

切片仅存储指向堆数据的指针以及长度信息。它不包含容量,因为它不拥有堆上的任何东西。

由于字符串引用包含用作切片的所有信息(指向堆数据的指针 + 长度),Rust 允许在需要字符串切片的地方使用字符串引用,这个便利性就是 deref coercion

fn main() {
    let message = String::from("lorem ipsum");
   
    // notice that a string reference is passed as argument
    let first_word = get_first_word(&message); 
    println!("first_word is \"{}\"", first_word);
}
// notice that the expected argument is of type string slice (&str)
fn get_first_word(msg: &str) -> &str {
    let bytes: &[u8] = msg.as_bytes();
    for (index, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &msg[..index];
        }
    }
    &msg
}

当然,当需要字符串引用,而使用字符串切片时,deref coercion 不起作用(因为缺少属性)

编写代码时,在不需要数据所有权的情况下更喜欢使用字符串切片,即大家更喜欢使用 &str

16、多维数组

fn main() {
    let d2: [[i32; 3]; 3] = [[9, 8, 7], [6, 5, 4], [3, 2, 1]];
    let value = d2[1][0];
    println!("value is {}", value);
    // mutating a tuple
    let d3: [[[&str; 100]; 20]; 5];
    d3 = [[["ok"; 100]; 20]; 5];
    println!("value d3[3][11][35] is {}", d3[3][11][35])
}

输出:

value is 6
value d3[3][11][35] is ok

17、向量

fn main() {
    // collection of elements with the same data type
    // elements are sorted in order
    // arrays have a fixed size that must be known at compile time
    // because array data is stored on the stack
    // vectors can dynamically grow and shrink
    // by adding / removing items
    // vector data is stored in heap memory
    // therefore you need to handle ownership and borrowing
    // vectors = mutable size arrays
    let mut letters: Vec<char> = vec!['a', 'b', 'c'];
    println!("letters are {:?}", letters);
    let first_letter = letters[0];
    println!("first_letter is {}", first_letter);
    // add value to vector
    letters.push('d');
    letters.push('e');
    letters.push('f');
    println!("letters are {:?}", letters);
    // remove last value
    letters.pop();
    println!("letters are {:?}", letters);
    let mut numbers: Vec<i32> = vec![11, 22, 44];
    numbers[2] = 33;
    println!("numbers is {}", numbers[2]);
    let words: Vec<&str>;
    words = vec!["ok"; 2];
    println!("words are {:?}", words);
    let mut ints = vec![22, 33, 44, 55, 66, 77];
    let length: usize = ints.len();
    println!("length is {}", length);
    let mem_size_byte = std::mem::size_of_val(&ints);
    println!("mem_size_byte is {}", mem_size_byte);
    // slice from vector
    let mut slice: &[i32] = &ints;
    println!("slice is {:?}", slice);
    slice = &ints[2..5];
    println!("slice is {:?}", slice);
    // iterate over vector
    for it in ints.iter() {
        println!("it is {}", it);
    }
    // mutate vector items while iterating
    for it in ints.iter_mut() {
        // dereference the pointer to get and set value (*it)
        *it *= *it;
    }
    println!("ints is {:?}", ints);
}

输出结果:

letters are ['a', 'b', 'c']
first_letter is a
letters are ['a', 'b', 'c', 'd', 'e', 'f']
letters are ['a', 'b', 'c', 'd', 'e']
numbers is 33
words is ["ok", "ok"]
length is 6
mem_size_byte is 24
slice is [22, 33, 44, 55, 66, 77]
slice is [44, 55, 66]
it is 22
it is 33
it is 44
it is 55
it is 66
it is 77
ints is [484, 1089, 1936, 3025, 4356, 5929]

18、元组

fn main() {
    // used to group related items of mixed data types
    // can have max 12 mixed type values 
    // adding more values and it will no longer be a tuple type
    let a_tuple: (&str, u8, char) = ("ok", 0, 'd');
    let first_item = a_tuple.0;
    println!("first_item is {}", first_item);
    // mutate a tuple
    let mut b_tuple = ("ok", 0);
    b_tuple.0 = "ko";
    b_tuple.1 += 1;
    println!("b_tuple.1 is {}", b_tuple.1);
    // destructure a tuple
    let c_tuple = ("en", "US", 1);
    let (language, country, code) = c_tuple;
    println!(
        "language is: {}\ncountry is: {}\ncode is: {}",
        language, country, code
    )
}

输出结果:

first_item is ok
b_tuple.1 is 1
language is: en
country is: US
code is: 1

19、函数

fn main() {
    be_polite();
    // inferred types for y and z are the ones used as parameters of add()
    // to be clear, if you do not declare a specific type for variables, these variables will assume the type of the arguments of the function where first used
   // remember, by the default inferred type is i32 for integers
    let y = 12;
    let z = 34;
    // now y and z are considered u8 type because this is how they are first used as function arguments
    add(y, z);
    // passing later y and z to another fn with different param types will panic
    // guess_number(z) // -> expects a i32 not a u8
    // need for explicit cast:
    guess_number(y as i32)
}
fn be_polite() {
    println!("Greetings, pleased to meet you.");
    guess_number(25)
}
fn guess_number(number: i32) {
    println!("Indeed, {} is the correct answer", number)
}
fn add(a: u8, b: u8) {
    let sum = a + b;
    println!("sum is {}", sum)
}

输出结果:

Greetings, pleased to meet you.
Indeed, 25 is the correct answer
sum is 46
Indeed, 12 is the correct answer

20、语句和表达式

fn main() {
    // Statement performs an action without returning a value
    // statements end with a semicolon: a = 6;
    // an expression evaluates to a resulting value
    // expressions do NOT end with a semicolon: 3 + 4 which evaluates to 7
    // adding a semicolon to an expressions transforms it into an statement
    // expressions are used as parts of statements: let total = r + c;\n\t{}\n\t{}",
    // where "r + c" is an expression and "let total = r + c;" is a statement
    println!("expression 4 + 5 evaluates to: {}", 4 + 5);
}

输出结果:

expression 4 + 5 evaluates to: 9

21、函数返回类型

fn main() {
    let result = square(3);
    println!("result is {}", result);
    let result_tuple = triple(33);
    let (input, result1) = result_tuple;
    println!("result_tuple is {:?}", result_tuple); 
    // {:?} ==> debug formatting
    println!("input {} evaluates to {}", input, result1);
    let nothing: () = does_not_return();
    println!("nothing (union data type) is {:?}", nothing)
}
fn square(number: i32) -> i32 {
    println!("processing square({})", number);
    // expression returning a value
    number * number
    // " return  number * number;" is also valid syntax
}
// multiple returns with tuples
fn triple(number: i32) -> (i32, i32) {
    println!("tripling the number: {}", number);
    let input = number;
    let result = number * 3;
    (input, result)
}
// union data type
// used when no meaningful values returned by a fn
// represented by empty ()
// it is optional
fn does_not_return() -> () {
    println!("ain't returning nuthing!")
}

输出结果:

processing square(3)
result is 9
tripling the number: 33
result_tuple is (33, 99)
input 33 evaluates to 99
ain't returning nuthing!
nothing (union data type) is ()

22、闭包

fn main() {
    // closures are anonymous functions that have access to variables in the enclosing scope
    // long form
    let double = |n1: u8| -> u8 { n1 * 2 };
    // short form
    let triple = |n1| n1 * 3;
    const DAYS_IN_YEAR: u16 = 365;
    // referencing variable from enclosing scope
    let quadruple_than_add_number_days_in_year = |n1: i32| n1 * 4 + (DAYS_IN_YEAR as i32);
    const FACTOR: i32 = 22;
    let multiple_by_22 = |x| FACTOR * x;
    println!("{}", double(11));
    println!("{}", triple(99));
    println!("{}", quadruple_than_add_number_days_in_year(44));
    println!("{}", multiple_by_22(5));
}

输出结果:

23、摄氏度到华氏度转换

fn main() {
    let (celsius, farenheit) = to_farenheit(40.0);
    println!("{} celsius is {} farenheit", celsius, farenheit);
    assert_eq!(farenheit, 104.0);
    // will not execute if assertion fails
    println!("test passed");
}
fn to_farenheit(celsius: f32) -> (f32, f32) {
    let farenheit = (1.8 * celsius) + 32.0;
    // return statement (no semicolon)
    (celsius, farenheit)
}

24、条件执行

fn main() {
    let x = 5;
    if x == 5 {
        println!("x is 5");
    }
    // if expressions (equivalent of ternary operator in JS/Node.js)
    let x_odd = if x % 2 == 0 { "odd" } else { "even" };
    println!("x_odd is {}", x_odd);
}

输出结果:

x is 5
x_odd is even

25、多重条件(if/else if)

n main() {
    let x = 2;
    let y = 5;
    if x > y {
        println!("x is greater than  y");
    } else if x < y {
        println!("x is less than y");
    } else {
        println!("x is equal to y");
    }
}

输出结果:

x is less than y

26、循环赋值

fn main() {
    let mut count = 0;
    // infinite loop
    loop {
        if count == 10 {
            break;
        }
        count += 1;
        println!("count is {}", count);
    }
    println!("\nAfter first loop.\n");
    // returning a value from loop expression
    let result = loop {
        if count == 15 {
            // returning a value with break statement
            break count * 20;
        }
        count += 1;
        println!("count is {}", count);
    };
    println!("\nAfter second loop, result is {}", result);
}

输出结果:

count is 1
count is 2
count is 3
count is 4
count is 5
count is 6
count is 7
count is 8
count is 9
count is 10
After first loop.
count is 11
count is 12
count is 13
count is 14
count is 15
After second loop, result is 300

27、while循环

fn main() {
    let mut count = 0;
    let letters: [char; 5] = ['a', 'b', 'c', 'd', 'e'];
    while count < letters.len() {
        println!("letter[{}] is {}", count, letters[count]);
        count += 1;
    }
// contrary to loop expressions, the break statement in while loop cannot return a value
}

输出结果:

letter[0] is a
letter[1] is b
letter[2] is c
letter[3] is d
letter[4] is e

28、for循环

fn main() {
    let message = ['m', 'e', 's', 's', 'a', 'g', 'e'];
    /* Iterator
    - implements logic to iterate over each item in a collection
    - next() method returns the next item in a sequence
      */
    for item in message.iter() {
        println!("current item is {}", item);
    }
    println!("");
    // To also get the indexes when iterating
    // enumerate() returns a tuple with index/item_reference pair
    // To get the item use &item
    // because the iterator gives back a reference (&<NAME>)
    // Adding the & (borrow operator) allows you to 
    // borrow the variable without 
    // taking ownership (see borrowing section) 
    // - then when you use the variable in the for loop scope, you access the value
    for (index, &item) in message.iter().enumerate() {
        println!("item {} is {}", index, item);
        if item == 'e' {
            break;
        }
    }
    println!("");
    // iterating over a range of numbers
    // excludes the end value of the range
    for number in 0..5 {
        println!("number is {}", number);
    }
}

输出结果:

current item is m
current item is e
current item is s
current item is s
current item is a
current item is g
current item is e
item 0 is m
item 1 is e
number is 0
number is 1
number is 2
number is 3
number is 4

29、嵌套循环

fn main() {
    let mut matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
    // reading from matrix
    for row in matrix.iter() {
        for number in row.iter() {
            print!("{}\t", number);
        }
        println!("");
    }
    println!("=======================");
    // modifying values from mutable matrix
    // iter_mut() returns mutable references
    for row in matrix.iter_mut() {
        for number in row.iter_mut() {
            // dereference with asterisk to get the value itself
            *number += 20;
            print!("{}\t", number);
        }
        println!("");
    }
}

输出结果:

1 2 3 
4 5 6 
7 8 9 
=======================
21 22 23 
24 25 26 
27 28 29

30、猜数游戏

use rand::Rng;
use std::io;
fn main() {
    println!("Guess a number");
    println!("Please enter your guess:");
    let secret_number = rand::thread_rng().gen_range(1, 101);
    println!("The secret number is {}", secret_number);
    // "::" is used for associated functions of a given type (equiv to static methods in OOP - more on this later)
    // String::new() creates an empty string of type String    (growable UTF-8 encoded text)
    let mut guess = String::new();
    /*
        std::io::stdin, if you don't use the import at the top of file
        std::io::stdin() returns an instance of a std::io::Stdin type
    */
    io::stdin()
        .read_line(&mut guess)
        .expect("Failed to read line");
    println!("You guess: {}", guess);
}

31、统计基础

fn main() {
    let numbers = [1, 9, -2, 0, 23, 20, -7, 13, 37, 20, 56, -18, 20, 3];
    let mut max: i32 = numbers[0];
    let mut min: i32 = numbers[0];
    let mut mean: f64 = 0.0;
    for item in numbers.iter() {
        mean += *item as f64;
        if *item > max {
            max = *item;
        }
        if *item < min {
            min = *item;
        }
    }
    mean /= numbers.len() as f64;
    assert_eq!(max, 56);
    assert_eq!(min, -18);
    assert_eq!(mean, 12.5);
    println!("Test passed!");
}

输出结果:

Test passed!

32、作用域

fn main() {
    let planet = "Dunya";
    if true {
        let planet = "Jupiter";
        println!("planet is {}", planet);
    }
    println!("planet is {}", planet);
}

输出结果:

planet is Jupiter
planet is Dunya

33、量可变性

fn main() {
    let car = "Mitsubishi";
    println!("car is a {}", car);
    // code block, has its own scope
    {
        // varable shadowing
        let car = 1;
        println!("car is a {}", car);
    }
    println!("car is a {}", car);
}

输出结果:

car is a Mitsubishi
car is a 1
car is a Mitsubishi

34、栈和堆

fn main() {
    println!("=== STACK ====\n");
    println!("- values stored in sequential order of insertion");
    println!("- data added in LIFO (last in first out)");
    println!("- stores variables - pushing values on the stack");
    println!("- also holds info for function execution");
    println!(
        "- stack have very fast access because no guessing where to put data, it will be on top"
    );
    println!("- stacks are limited in size");
    println!("- all data in stack must have a known fixed size\n");
    func1();
    println!("func1 done");
    println!("pop variable y off the stack");
    println!("pop variable z off the stack\n");
println!("\n\n=== HEAP ====\n");
    println!("- adding data to heap, search for large enough place in memory to store data");
    println!("- marks memory spot as being used (allocating) and put data in it");
    println!("- accessing data in heap is more complex than the stack because the stack allocates anywhere in available memory");
    println!("- slower than stack");
    println!("- dynamically add and remove data");
    println!("\n\n=== POINTER ====\n");
    println!("- data type that stores a memory address");
    println!("- pointers have a fixed size so can be stored on the stack");
    println!("- adding and accessing data on the heap is done through pointers (addresses in memory)");
}
fn func1() {
    println!("func1 executing...");
    let y = 3.11;
    println!("push variable y = {} onto the stack", y);
    let z = 5;
    println!("push variable z = {} onto the stack", z);
    func2();
    println!("func2 done");
    println!("pop variable arr off the stack");
}
fn func2() {
    println!("func2 executing...");
    let arr = [2, 3, 4];
    println!("push variable arr = {:?} onto the stack", arr);
}

输出结果:

=== STACK ====
- values stored in sequential order of insertion
- data added in LIFO (last in first out)
- stores variables - pushing values on the stack
- also holds info for function execution
- stack have very fast access because no guessing where to put data, it will be on top
- stacks are limited in size
- all data in stack must have a known fixed size
func1 executing...
push variable y = 3.11 onto the stack
push variable z = 5 onto the stack
func2 executing...
push variable arr = [2, 3, 4] onto the stack
func2 done
pop variable arr off the stack
func1 done
pop variable y off the stack
pop variable z off the stack
=== HEAP ====
- adding data to heap, search for large enough place in memory to store data
- marks memory spot as being used (allocating) and put data in it
- accessing data in heap is more complex than the stack because the stack allocates anywhere in available memory
- slower than stack
- dynamically add and remove data
=== POINTER ====
- data type that stores a memory address
- pointers have a fixed size so can be stored on the stack
- adding and accessing data on the heap is done through pointers (addresses in memory)

35、字符串

Rust有两种字符串类型。

fn main() {
    // Two types of string representation:
    
    // - string literals: hard coded into the executable.
    // these are immutable and must be known before compilation
    
    // - String type: allocated data on the heap, \n\tmutable and dynamically generated at runtime
    // string literal stored on heap
    // String::from() creates a String type from a string literal
    // the sequence [m,a,r,s] will get stored on the heap
    // to access the string stored on heap, program holds a pointer to it on the stack (message variable)
    // that pointer on the stack includes first char memory address, length of string and the capacity so you know how much memory s allocated for it on the heap
    let mut message = String::from("Jupiter");
    println!("message is {}", message);
    // append string to original
    // if more memory need than capacity, pointer address updated as well as length and capacity to reflect new location in memory
    message.push_str(" is smoke and mirrors");
    println!("message is {}", message);
    // pushing a char
    message.push('!');
    println!("message is {}", message);
   // get length
   println!("message lenght is {}", message.len());
   // get capacity in bytes
   println!("message capacity is {}", message.capacity());
  // check if empty
  println!("Is empty: {}", message.is_empty());
  // substring search
  println!("Contains smoke: {}", message.contains("smoke"));
  // replace substring
  println!("message is {}", message.replace("smoke","gaz"));
  // loop over words in string (split by white space)
  for word in message.split_whitespace() {
    println!("word is {}", word);
  }
  // create string with capacity
  let mut s = String::with_capacity(4); // 4 bytes capacity
  println!("s capacity is  {} bytes", s.capacity());
  // 1 byte consumed 
  // Latin alphabet letters usually have 1 byte size
  // remember Unicode supports 4-byte characters
  s.push('Q');
  s.push('W'); // 1 byte consumed
  s.push_str("er"); // 2 bytes consumed
  // exceeding string capacity (automagically increased and reallocation in memory)
  s.push('T'); // 1 byte consumed
  println!("s capacity is  now {} bytes", s.capacity());
}

输出结果:

message is Jupiter
message is Jupiter is smoke and mirrors
message is Jupiter is smoke and mirrors!
message lenght is 29
message capacity is 56
Is empty: false
Contains smoke: true
message is Jupiter is gaz and mirrors!
word is Jupiter
word is is
word is smoke
word is and
word is mirrors!
s capacity is  4 bytes
s capacity is  now 8 bytes

36、所有权

fn main() {
    /* need to clean up allocated memory blocks no longer needed
    in C/C++: malloc() and free() for manual memory mngt
    other approach is garbage collection which is automatic */
    /*
    Rust uses OWNERSHIP ystem:
       - variables are responsible for freeing their own resources
       - every value is owned by only one variable at a time
       - when owning variable goes out of scope the value is dropped
       - there are ways to transfer ownership of a value from one variable to another
    */
    let outer_planet: String;
    let outer_galaxy: String;
    let outer_planet_position: i32;
    // inner code block scope
    {
        let inner_planet = String::from("Mercury");
        println!("inner_planet is {}", inner_planet);
        /*
        because ownership mandates only one owner per value/data,
         - inner_planet will no longer point to the String value on the heap
         - transferring ownership from one variable to another is called a "move" in Rust
         - this means that NO shallow copy of data STORED ON THE HEAP in Rust
            (shallow copy = several variables pointing to same data in memory)
        */
        // transferring ownership
        outer_planet = inner_planet;
        // can no longer use inner_planet variable after the move of ownership of string data
        // println!("inner_planet is {}", inner_planet); // => will panic
        let mut inner_galaxy = String::from("Milky Way");
        println!("inner_galaxy is {}", inner_galaxy);
        // to duplicate data stored on the heap, creates a deep copy of the String data
        
        outer_galaxy = inner_galaxy.clone();
        inner_galaxy.clear();
        println!("inner_galaxy is now: {}", inner_galaxy);
        println!("outer_galaxy is {}", outer_galaxy);
        // integer data types live on the stack
        let mut inner_planet_position = 1;
        println!("inner_planet_position is {}", inner_planet_position);
        /*
        a copy of the integer data is created for the outer_planet_position
        - ownership is respected (no shallow copy - only one variable per value at a time)
        - generally STACK-ONLY data types (ie fixed size) are implicitly copied
            when variable containing them is assigned to another variable
        - data types stored om stack implement the trait that allow them to be copied rather than moved
        */
        outer_planet_position = inner_planet_position;
        inner_planet_position += 4;
        println!("inner_planet_position is {}", inner_planet_position);
        println!("outer_planet_position is {}", outer_planet_position);
    }
    println!("\nouter_planet is {}", outer_planet);
    println!("outer_galaxy is {}", outer_galaxy);
    println!("outer_planet_position is {}", outer_planet_position);
}

输出结果:

inner_planet is Mercury
inner_galaxy is Milky Way
inner_galaxy is now: 
outer_galaxy is Milky Way
inner_planet_position is 1
inner_planet_position is 5
outer_planet_position is 1

37、所有权转移

fn main() {
    let rocket_fuel = 1;
    process_fuel(rocket_fuel);
    println!("rocket_fuel is {}", rocket_fuel);
}
/*
    - because propellant is i32 so lives on the stack, 
    the value passed as argument is COPIED jn fn scope
    - to be able to modify the copy inside the function scope, use the mut keyword
*/
fn process_fuel(mut propellant: i32) {
    // the copy is modified
    propellant += 2;
    println!("Processing propellant {}", propellant);
}

输出结果:

Processing propellant 3
rocket_fuel is 1

38、结构体

用于对混合数据类型的多个相关项进行分组

元素被命名(不像它们被排序的元组)

两种结构体(regular struct 和 tuple struct)

// tuple struct
// used to store a collection of mixed data without named fields
// used to be distinguished as a specific type 
// (not just a regular tuple)
struct Signal(u8, bool, String);

// regular struct
// struct names are capitalized 
// like classes in JavaScript and OOP generally
struct Car {
    // fields of the struct
    model: String,
    year: String,
    used: bool,
}
// method: functions/subroutines associated to a struct
// methods are defined within the context of a struct
// the first parameter of a method is the reference to a struct instance
impl Car {
    // construct car
    fn new(m: &str, y: &str) -> Car {
        Car {
            model: m.to_string(),
            year: y.to_string(),
            used: false,
        }
    }
    // self is equivalent to "this" is JavaScript
    fn serialize(&self) -> String {
        format!(
            "model: {} - year: {} - used: {}",
            self.model, self.year, self.used
        )
    }
    // mutate state
    fn marked_used(&mut self) {
        self.used = true;
    }
}
struct Position {
  latitude: f64,
  longitude: f64
}
fn print_signal(s: &Signal) {
    println!("s1 is {}, {}, {}", s.0, s.1, s.2);
}
fn main() {
    let mut pos_1 = Position {
        latitude: 27.299112,
        longitude: 95.387110,
    };
    println!(
      "pos_1 is {:.3}, {:.3}", 
      pos_1.latitude, 
      pos_1.longitude
    );
    pos_1.latitude = 23.1111;
    println!(
      "pos_1 is now {:.3}, {:.3}",
      pos_1.latitude, 
      pos_1.longitude
    );
    let mut s1 = Signal(0, true, String::from("ok"));
    // fields of a tuple struct are accessed like regular tuples values
    // using their index
    // remember tuple structs do not have named fields
    print_signal(&s1);
    s1.0 = 23;
    s1.1 = false;
    s1.2 = String::from("NETERR");
    println!("s1 is now {}, {}, {}", s1.0, s1.1, s1.2);
    let car_1 = Car::new("QBC", "2133");
    println!("car_1 is a {} of {}", car_1.model, car_1.year);
    let is_used = if car_1.used == true {
        "used"
    } else {
        "brand new"
    };
    println!("car_1 is {}", is_used);
    println!("car_1 is {}", car_1.serialize());
    let mut car_2 = Car::new("ZZ7", "2042");
    println!("car_2 is a {}", car_2.serialize());
    car_2.marked_used();
    println!("car_2 is now {}", car_2.serialize());
}

输出结果:

pos_1 is 27.299, 95.387
pos_1 is now 23.111, 95.387
s1 is 0, true, ok
s1 is now 23, false, NETERR
car_1 is a QBC of 2133
car_1 is brand new
car_1 is model: QBC - year: 2133 - used: false
car_2 is a model: ZZ7 - year: 2042 - used: false
car_2 is now model: ZZ7 - year: 2042 - used: true

39、枚举

// defines a data type with multiple possible variants
enum Controller {
    Turbo,
    Up,
    Down,
    Left,
    Right,
    X,
    Y,
    A,
    B,
}
fn push_button_notify(c: &Controller) {
    // pattern matching (equivalent to switch in JavaScript)
    match c {
        Controller::Turbo => println!("Turbo button pushed."),
        Controller::Up => println!("Up button pushed."),
        Controller::Down => println!("Down button pushed."),
        Controller::Left => println!("Left button pushed."),
        Controller::Right => println!("Right button pushed."),
        Controller::Y => println!("Y button pushed."),
        Controller::X => println!("X button pushed."),
        Controller::A => println!("A button pushed."),
        Controller::B => println!("B button pushed."),
    }
}
fn main() {
    let secret_push_combo = [
        Controller::Up,
        Controller::Left,
        Controller::A,
        Controller::Turbo,
        Controller::Y,
        Controller::B,
        Controller::Turbo,
        Controller::Down,
        Controller::Right,
        Controller::X,
    ];
    for push in secret_push_combo.iter() {
        push_button_notify(push);
    }
}

输出结果:

Up button pushed.
Left button pushed.
A button pushed.
Turbo button pushed.
Y button pushed.
B button pushed.
Turbo button pushed.
Down button pushed.
Right button pushed.
X button pushed.

希望大家自己动手敲上面的代码。

原文链接:https://itnext.io/deep-dive-into-rust-for-node-js-developers-5faace6dc71f


链接: https://fly63.com/article/detial/11080

关于 Node.js 里 ES6 Modules 的一次更新说明

关于 Node.js 里 ES6 Modules 的一次更新说明,总结来说:CommonJS 与 ES6 Modules 之间的关键不同在于代码什么时候知道一个模块的结构和使用它。

用node.js开发一个可交互的命令行应用

在这个教程中,我们会开发一个命令行应用,它可以接收一个 CSV 格式的用户信息文件,教程的内容大纲:“Hello,World”,处理命令行参数,运行时的用户输入,异步网络会话,美化控制台的输出,封装成 shell 命令,JavaScript 之外

Node启动https服务器

首先你需要生成https证书,可以去付费的网站购买或者找一些免费的网站,可能会是key或者crt或者pem结尾的。不同格式之间可以通过OpenSSL转换

nodejs 异步转同步

nodej项目在微信环境开发,nodejs的异步特效,会导致请求没有完成就执行下面的代码,出现错误。经过多方查找,可以使用async模块来异步转同步,只有前一个function执行callback,下一个才会执行。

基于node服务器的大文件(G级)上传

3G的大文件分1500个2M二进度文件,通post方法发送给node服务,服务器全部接收到文件后,进组装生成你上文件。

为什么要把 JavaScript 放到服务器端上运行?

JavaScript比C的开发门槛要低,尽管服务器端JavaScript存在已经很多年了,但是后端部分一直没有市场,JavaScript在浏览器中有广泛的事件驱动方面的应用,考虑到高性能、符合事件驱动、没有历史包袱这3个主要原因,JavaScript成为了Node的实现语言。

了解node.js事件循环

node.js的第一个基本论点是I / O的性能消耗是很昂贵。因此,使用当前编程技术的最大浪费来自于等待I / O完成。有几种方法可以处理性能影响

Node.js 应用:Koa2 使用 JWT 进行鉴权

在前后端分离的开发中,通过 Restful API 进行数据交互时,如果没有对 API 进行保护,那么别人就可以很容易地获取并调用这些 API 进行操作。那么服务器端要如何进行鉴权呢?

Node.js 前端开发指南

我们经常跟Node.js打交道,即使你是一名前端开发人员 -- npm脚本,webpack配置,gulp任务,程序打包 或 运行测试等。即使你真的不需要深入理解这些任务,但有时候你会感到困惑,会因为缺少Node.js的一些核心概念而以非常奇怪的方式来编码。

happypack提升项目构建速度

运行在 Node.js 之上的 Webpack 是单线程模型的,也就是说 Webpack 需要处理的任务需要一件件挨着做,不能多个事情一起做。happypack把任务分解给多个子进程去并发的执行,子进程处理完后再把结果发送给主进程。

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!