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

2021年6月1日 241点热度 0人点赞 0条评论

图片

作者 | Florian GOTO       
译者 | 弯月    责编 | 欧阳姝黎
出品 | CSDN(ID:CSDNnews)

以下为译文:

随着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,因此编程难度并不高。

图片

创建一个Rust项目


本文所有的代码都可以利用Rust playground在线运行(当然,除了那些需要访问本地的代码之外)。

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

cargo new <PROJECT_NAME>

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

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

cargo init源代码位于src/目录下,入口为main.rs文件中的main函数(用fn关键字定义)。fn main() {println!("Hello, world!");}

图片

输出


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

fn main() {// string interpolationprintln!("Adding {} and {} gives {}", 22, 33, 22 + 33);// positional argumentsprintln!("Ypur name is {0}. Welcome to {1}. Nice to meet you {0}","Goto", "Rust");// named argumentsprintln!("{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 typeprintln!("{:?}", [11, 22, 33]);}

运行代码查看输出:

cargo run

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

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

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

图片

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


fn main() {let x = 246.92385;let y = 24.69;let z = x / y;// print line macro with 3 decimal point precisionprintln!("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 charactersprintln!("z is {:09.3}", z);println!("z is {:09.3}\nx is {}", z, x);// print macro without new lineprint!("y is {:09.3}\n x is {}\n", y, x);// positional parametersprintln!("z is {0:05.1} and x is {1:.2}. \nx is also {1}", z, x)}

输出结果:

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

图片

变量


fn main() {// variables are immutable by defaultlet pc = "Inspirion XYZ";println!("pc is {}", pc);// mutable variableslet 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 articlelet (status, code) = ("OK", 200);println!("status: {}, code: {}", status, code);}

输出结果:

pc is Inspirion XYZage is 1age is 2brand is Dellstatus: OK, code: 200

图片

基本类型


fn main() {// default integer numeric type is i32let num1 = 123;println!("{} - type: {}", num1, get_type(&num1));// default floating point numeric type is f64let num2 = 1.23;println!("{} - type: {}", num2, get_type(&num2));// explicit typinglet 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 propertieslet max_i32 = std::i32::MAX;let max_i16 = std::i16::MAX;println!("max value for i32 is {}", max_i32);println!("max value for i16 is {}", max_i16);// booleanlet 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 = '?';println!("smiley is {} - type: {}", smiley, get_type(&smiley));}// helper function to print typesfn get_type<T>(_: &T) -> &str {std::any::type_name::<T>()}

输出结果:

123 - type: i321.23 - type: f6423 - type: i8max value for i32 is 2147483647max value for i16 is 32767is_rust_fun is true - type: boolis_greater is true - type: boolsmiley is ? - type: char

图片

浮点数


  • 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 = std::f32::MIN;println!("min_f32 is: {}\n", min_f32);let max_f32 = std::f32::MAX;println!("max_f32 is: {}\n", max_f32);let min_f64 = std::f64::MIN;println!("min_f64 is: {}\n", min_f64);let max_f64 = std::f64::MAX;println!("max_f64 is: {}\n", max_f64);}

输出结果:

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

图片

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


/*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);/*:08b0 -> display leading zeros8 -> number of bits to displayb -> display binary representation*/println!("value is {:08b}", value);// bitwise NOT: invert individual bits
value = !value; // 0000_1010println!("value is {:08b}", value);// bitwise AND: used to clear the value of a specific bitvalue = value & 0b1111_0111; // -> 0000_0010println!("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_0010println!("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_0111println!("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_0000println!("value is {:08b}", value);// shift right by 3 bits
value = value >> 3; // -> 0000_1110println!("value is {:08b}", value);

输出结果:

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

图片

布尔和二进制代数


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 1println!("a XOR b is {}", (a ^ b) as i32); // 1let c = (a ^ b) | (a & b);println!("c is {}", c);// short-circuiting logical operations:// right operand not evaluatedlet 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 trueb is falseNOT a is falsea AND b is falsea OR b is truea XOR b is truea XOR b is 1c is trued is trueis false

图片

算术操作


fn main() {// can only do arithmetic operations on same type operandslet 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 44d is 11e is 121f is 9.777778is 43.5432

图片

比较操作


/*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 11b is 88a EQUAL TO b is falsea NOT EQUAL TO b is truea GREATER THAN b is falsea GREATER THAN OR EQUAL TO b is falsea LESS THAN b is truea LESS THAN OR EQUAL TO b is truec is trued is falsec EQUAL TO d is falsec NOT EQUAL TO d is truec GREATER THAN d is truec GREATER THAN OR EQUAL TO d is truec LESS THAN d is falsec LESS THAN OR EQUAL TO d is false

图片

字符


fn main() {// Unicode scalar value stored using 4 bytes (32 bits)// contrary to C like languages that store it in 1 bytelet 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 znumber_char is 9finger is ☝

图片

计算平均值


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.8test passed.

图片

数组


fn main() {// fixed length and single typed// stored in contiguous memory locationslet 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 mutablelet 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 arraylet mut slice: &[i32] = &ints;println!("slice is {:?}", slice);
slice = &ints[3..5];println!("slice is {:?}", slice);}

输出结果:

first_letter is anumbers is 33words is ["ok", "ok"]length is 5mem_size_byte is 20slice is [22, 22, 22, 22, 22]slice is [2222]

图片

多维数组


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 tuplelet d3: [[[&str; 100]; 20]; 5];d3 = [[["ok"; 100]; 20]; 5];println!("value d3[3][11][35] is {}", d3[3][11][35])}

输出结果:

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

图片

向量


fn main() {// vectors = mutable size arrayslet 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 vectorletters.push('d');letters.push('e');letters.push('f');println!("letters are {:?}", letters);// remove last valueletters.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 vectorlet mut slice: &[i32] = &ints;println!("slice is {:?}", slice);slice = &ints[2..5];println!("slice is {:?}", slice);// iterate over vectorfor it in ints.iter() {println!("it is {}", it);}// mutate vector items while iteratingfor 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 aletters are ['a', 'b', 'c', 'd', 'e', 'f']letters are ['a', 'b', 'c', 'd', 'e']numbers is 33words is ["ok", "ok"]length is 6mem_size_byte is 24slice is [22, 33, 44, 55, 66, 77]slice is [44, 55, 66]it is 22it is 33it is 44it is 55it is 66it is 77ints is [48410891936302543565929]

图片

元组


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

输出结果:

first_item is okb_tuple.1 is 1language is: encountry is: UScode is1

图片

函数


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 integerslet y = 12;let z = 34;// now y and z are considered u8 type because this is how they are first used as function argumentsadd(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 answersum is 46Indeed, 12 is the correct answer

图片

语句和表达式


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 statementprintln!("expression 4 + 5 evaluates to: {}", 4 + 5);}

输出结果:

expression 4 + 5 evaluates to: 9

图片

函数返回类型


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 formattingprintln!("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 valuenumber * number// " return  number * number;" is also valid syntax}// multiple returns with tuplesfn 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 optionalfn does_not_return() -> () {println!("ain't returning nuthing!")}

输出结果:

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

图片

闭包


fn main() {// closures are anonymous functions that have access to variables in the enclosing scope// long formlet double = |n1: u8| -> u8 { n1 * 2 };// short formlet triple = |n1| n1 * 3;const DAYS_IN_YEAR: u16 = 365;// referencing variable from enclosing scopelet 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));}

输出结果:

22297541110

图片

摄氏度到华氏度转换


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 failsprintln!("test passed");}fn to_farenheit(celsius: f32) -> (f32, f32) {let farenheit = (1.8 * celsius) + 32.0;// return statement (no semicolon)(celsius, farenheit)}

图片

条件执行


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 5x_odd is even

图片

多重条件(if/else if)


fn 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

图片

循环赋值


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

输出结果:

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

图片

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 aletter[1] is bletter[2] is cletter[3] is dletter[4is e

图片

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>)// if you don't use the &, you get the reference not the value// adding the & allows you to borrow the variable without taking ownership (more on that later) - then when you use the variable in the for loop scope, you access the valuefor (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 rangefor number in 0..5 {println!("number is {}", number);}}

输出结果:

current item is mcurrent item is ecurrent item is scurrent item is scurrent item is acurrent item is gcurrent item is eitem 0 is mitem 1 is enumber is 0number is 1number is 2number is 3number is 4

图片

嵌套循环


fn main() {let mut matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];// reading from matrixfor row in matrix.iter() {for number in row.iter() {print!("{}\t", number);}println!("");}println!("=======================");// modifying values from mutable matrix// iter_mut() returns mutable referencesfor 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 34 5 67 8 9=======================21 22 2324 25 2627 28 29

图片

猜数游戏


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)// 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 filestd::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);}

图片

统计基础


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!

图片

作用域


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

输出结果:

planet is Jupiterplanet is Dunya

图片

变量可变性


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

输出结果:

car is a Mitsubishicar is a 1car is a Mitsubishi

图片

栈和堆


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 sizefunc1 executing...push variable y = 3.11 onto the stackpush variable z = 5 onto the stackfunc2 executing...push variable arr = [2, 3, 4] onto the stackfunc2 donepop variable arr off the stackfunc1 donepop variable y off the stackpop 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)

图片

字符串


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 heaplet 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 memorymessage.push_str(" is smoke and mirrors");println!("message is {}", message);// pushing a charmessage.push('!');println!("message is {}", message);// get lengthprintln!("message lenght is {}", message.len());// get capacity in bytesprintln!("message capacity is {}", message.capacity());// check if emptyprintln!("Is empty: {}", message.is_empty());// substring searchprintln!("Contains smoke: {}", message.contains("smoke"));// replace substringprintln!("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 capacitylet mut s = String::with_capacity(4); // 4 bytes capacityprintln!("s capacity is {} bytes", s.capacity());// 1 byte consumed// Latin alphabet letters usually have 1 byte size// remember Unicode supports 4-byte characterss.push('Q');s.push('W'); // 1 byte consumeds.push_str("er"); // 2 bytes consumed// exceeding string capacity (automagically increased and reallocation in memory)s.push('T'); // 1 byte consumedprintln!("s capacity is now {} bytes", s.capacity());}

输出结果:

message is Jupitermessage is Jupiter is smoke and mirrorsmessage is Jupiter is smoke and mirrors!message lenght is 29message capacity is 56Is empty: falseContains smoke: truemessage is Jupiter is gaz and mirrors!word is Jupiterword is isword is smokeword is andword is mirrors!s capacity is  4 bytess capacity is  now 8 bytes

图片

所有权


fn main() {/* need to clean up allocated memory blocks no longer neededin C/C++: malloc() and free() for manual memory mngtother 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 ownershipouter_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 paniclet mut inner_galaxy = String::from("Milky Way");println!("inner_galaxy is {}", inner_galaxy);// to duplicate data, 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 stacklet 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 copiedwhen 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 Mercuryinner_galaxy is Milky Wayinner_galaxy is now:outer_galaxy is Milky Wayinner_planet_position is 1inner_planet_position is 5outer_planet_position is 1

图片

所有权转移


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 is COPIED jn fn scope*/fn process_fuel(mut propellant: i32) {// the copy is modifiedpropellant += 2;println!("Processing propellant {}", propellant);}

输出结果:

Processing propellant 3rocket_fuel is 1fn main() {let mut arr_1: [u8; 2] = [33, 66];// ////////////////// fixed-length types (stored on the stack) are COPIED// ////////////////let arr_2 = arr_1;println!("arr_1 is {:?}", arr_1);arr_1 = [1, 2];println!("arr_1 is now {:?}", arr_1);println!("arr_2 is {:?}", arr_2);// ////////////////// mutable-length type values move the ownership to new variable// ////////////////let vec_1 = vec![3, 4];let vec_2 = vec_1;// can be no longer use the variable which ownership has been "moved"// println!("vec_1 is {:?}", vec_1); // => wll panicprintln!("vec_2 is {:?}", vec_2);// to borrow value owned by a variable without moving ownership,// use a reference to that valuelet vec_4 = vec![5, 6, 7];// borrowing value using a reference (&<NAME>)let vec_5 = &vec_4;println!("vec_4 is {:?}", vec_4);println!("vec_5 is {:?}", vec_5);}

输出结果:

arr_1 is [33, 66]arr_1 is now [1, 2]arr_2 is [33, 66]vec_2 is [3, 4]vec_4 is [5, 6, 7]vec_5 is [5, 6, 7]

图片

结构体


// used to create custom data typestruct Position {longitude: f64,latitude: f64,}// tuple structstruct Signal(u8, bool, String);struct Car {model: String,year: String,used: bool,}// associate functions to structimpl Car {// construct carfn new(m: &str, y: &str) -> Car {Car {model: m.to_string(),year: y.to_string(),used: false,}}// self is equivalent to "this" is JavaScriptfn serialize(&self) -> String {format!("model: {} - year: {} - used: {}",self.model, self.year, self.used)}// mutate statefn marked_used(&mut self) {self.used = true;}}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"));println!("s1 is {}, {}, {}", s1.0, s1.1, s1.2);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());}

输出结果:

os_1 is 27.299, 95.387pos_1 is now 23.111, 95.387s1 is 0, true, oks1 is now 23, false, NETERRcar_1 is a QBC of 2133car_1 is brand newcar_1 is model: QBC - year: 2133 - used: falsecar_2 is a model: ZZ7 - year: 2042 - used: falsecar_2 is now model: ZZ7 - year: 2042 - used: true

图片

枚举


enum Controller {Turbo,Up,Down,Left,Right,X,Y,A,B,}fn push_button_notify(c: &Controller) {// pattern mathing (equivalent to swith 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

声明:本文由CSDN翻译,转载请注明来源。

图片
生于2001年的《程序员》曾陪伴了无数开发者成长,影响了一代又一代的中国技术人。时隔20年,《新程序员》带着全球技术大师深邃思考、优秀开发者技术创造等深度内容回来了!同时将全方位为所有开发者呈现国内外核心技术生态体系全景图。扫描下方小程序码即可立即订阅!

图片

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

root

这个人很懒,什么都没留下

文章评论