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

发表于 2年以前  | 总阅读数:188 次

随着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 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中,行尾必须使用分号(;),除非是函数最后一行的返回语句(稍后进一步解释)。

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

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

变量

fn main() {
// variables are immutable by default
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

基本类型

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 = 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);
// 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 = '';
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  - 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.345677890123456
a_float is: 9.943853
min_f32 is: -340282350000000000000000000000000000000
max_f32 is: 340282350000000000000000000000000000000
min_f64 is: -179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
max_f64 is: 179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

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


/*
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

布尔和二进制代数

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

算术操作

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

比较操作


/*
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

字符

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 ☝

计算平均值

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.

数组

fn main() {
// fixed length and single typed
// stored in contiguous memory locations
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]

多维数组

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

向量

fn main() {
// 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]

元组


fn main() {
// 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

函数


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

语句和表达式


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

函数返回类型

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 ()

闭包

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));
}

输出结果:


22
297
541
110

摄氏度到华氏度转换


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)
}

条件执行

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

多重条件(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 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

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

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 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

嵌套循环


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

猜数游戏

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 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);
}

统计基础


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 Jupiter
planet is Dunya

变量可变性


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

栈和堆


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)

字符串

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

所有权


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, 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

所有权转移

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 modified
propellant += 2;
println!("Processing propellant {}", propellant);
}

输出结果:


Processing propellant 3
rocket_fuel is 1
fn 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 panic
println!("vec_2 is {:?}", vec_2);
// to borrow value owned by a variable without moving ownership,
// use a reference to that value
let 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 type
struct Position {
longitude: f64,
latitude: f64,
}
// tuple struct
struct Signal(u8, bool, String);
struct Car {
model: String,
year: String,
used: bool,
}
// associate functions to struct
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;
}
}
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.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

枚举

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.

本文由哈喽比特于2年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/RXU-xofTc0sipQGOQs2WQg

 相关推荐

刘强东夫妇:“移民美国”传言被驳斥

京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。

发布于:8月以前  |  808次阅读  |  详细内容 »

博主曝三大运营商,将集体采购百万台华为Mate60系列

日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。

发布于:8月以前  |  770次阅读  |  详细内容 »

ASML CEO警告:出口管制不是可行做法,不要“逼迫中国大陆创新”

据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。

发布于:8月以前  |  756次阅读  |  详细内容 »

抖音中长视频App青桃更名抖音精选,字节再发力对抗B站

今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。

发布于:8月以前  |  648次阅读  |  详细内容 »

威马CDO:中国每百户家庭仅17户有车

日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。

发布于:8月以前  |  589次阅读  |  详细内容 »

研究发现维生素 C 等抗氧化剂会刺激癌症生长和转移

近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。

发布于:8月以前  |  449次阅读  |  详细内容 »

苹果据称正引入3D打印技术,用以生产智能手表的钢质底盘

据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。

发布于:8月以前  |  446次阅读  |  详细内容 »

千万级抖音网红秀才账号被封禁

9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...

发布于:8月以前  |  445次阅读  |  详细内容 »

亚马逊股东起诉公司和贝索斯,称其在购买卫星发射服务时忽视了 SpaceX

9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。

发布于:8月以前  |  444次阅读  |  详细内容 »

苹果上线AppsbyApple网站,以推广自家应用程序

据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。

发布于:8月以前  |  442次阅读  |  详细内容 »

特斯拉美国降价引发投资者不满:“这是短期麻醉剂”

特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。

发布于:8月以前  |  441次阅读  |  详细内容 »

光刻机巨头阿斯麦:拿到许可,继续对华出口

据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。

发布于:8月以前  |  437次阅读  |  详细内容 »

马斯克与库克首次隔空合作:为苹果提供卫星服务

近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。

发布于:8月以前  |  430次阅读  |  详细内容 »

𝕏(推特)调整隐私政策,可拿用户发布的信息训练 AI 模型

据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。

发布于:8月以前  |  428次阅读  |  详细内容 »

荣耀CEO谈华为手机回归:替老同事们高兴,对行业也是好事

9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。

发布于:8月以前  |  423次阅读  |  详细内容 »

AI操控无人机能力超越人类冠军

《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。

发布于:8月以前  |  423次阅读  |  详细内容 »

AI生成的蘑菇科普书存在可致命错误

近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。

发布于:8月以前  |  420次阅读  |  详细内容 »

社交媒体平台𝕏计划收集用户生物识别数据与工作教育经历

社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”

发布于:8月以前  |  411次阅读  |  详细内容 »

国产扫地机器人热销欧洲,国产割草机器人抢占欧洲草坪

2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。

发布于:8月以前  |  406次阅读  |  详细内容 »

罗永浩吐槽iPhone15和14不会有区别,除了序列号变了

罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。

发布于:8月以前  |  398次阅读  |  详细内容 »
 相关文章
 目录