Rust 代码中的变量和函数名使用 snake case 风格,使用下划线分隔单词。Rust 是基于表达式的语言(expression-based),函数体由一系列语句(statement)和可选地结尾表达式(expression)组成,语句不会返回值而表达式会。表达式结尾没有分号,如果加上分号它就变为语句。如果函数没有返回值,则可以省略返回类型,会隐式地返回空元组,否则需要使用 -> 显式声明。代码块 {}和 if 都是表达式,可以在 let 语句右侧使用。
1 2 3
fnfive() ->i32 { 5 }
1 2 3
letcondition = true; letnumber = if condition { 5 } else { 6 }; println!("The value of number is: {number}");
{ lets = String::from("hello"); // s is valid from this point forward // do stuff with s } // this scope is now over, and s is no longer valid
所有权(Ownership)机制让 Rust 无需 GC 就能保证内存安全。GC 是运行时根据可达性分析回收内存,而所有权是在编译时确定变量的作用域,当内存的所有者变量离开作用域之后,相应内存就可以被释放。当变量离开作用域时,Rust 会自动调用 drop 函数来释放内存,类似 C++ 的 RAII 机制。
字符串 String 的数据存储在堆上,字符串变量包含指向堆中数据的指针、数据的长度和容量,其存储在栈上。使用 let s2 = s1; 只会复制引用(浅拷贝),为避免 s1 和 s2 离开作用域之后,释放相同内存两次(调用 drop 函数),该语句执行之后 Rust 会使 s1 无效,不需要在其离开作用域之后回收内存,该操作被称为移动(move)。
1 2 3
lets1 = String::from("hello"); lets2 = s1; println!("{s1}, world!"); // error[E0382]: borrow of moved value: `s1`
当给已有值的变量赋新值时,Rust 会立即调用 drop 释放原始值的内存。如果想要执行深拷贝,可以使用 clone 函数。如果类型实现 Copy 特征,则旧变量在被赋值给其他变量之后仍然有效。不能为实现 Drop 特征的类型实现 Copy 特征。
1 2 3
letmut s = String::from("hello"); s = String::from("ahoy"); println!("{s}, world!");
References and Borrowing
引用(reference)类似指针,它是一个地址,但是和指针不同,Rust 保证引用指向某个特定类型的有效值。传递引用值不会转移所有权,所以可以在 main 中继续使用 s1。函数 calculate_length 中的局部变量 s 是引用类型,其不拥有值的所有权,所以离开作用域之后,其指向的值也不会被回收,所以创建引用的行为被称为借用(borrowing)。
1 2 3 4 5 6 7 8 9
fnmain() { lets1 = String::from("hello"); letlen = calculate_length(&s1); println!("The length of '{s1}' is {len}."); }
if state.existed_in(1900) { Some(format!("{state:?} is pretty old, for America!")) } else { Some(format!("{state:?} is relatively new.")) } }
Common Collections
Storing Lists of Values with Vectors
1 2 3 4 5 6 7 8 9 10
letv = vec![1, 2, 3, 4, 5];
letthird: &i32 = &v[2]; println!("The third element is {third}");
letthird: Option<&i32> = v.get(2); match third { Some(third) => println!("The third element is {third}"), None => println!("There is no third element."), }
1 2 3 4
letmut v = vec![100, 32, 57]; foriin &mut v { *i += 50; }
Rust 中每个引用都有生命周期(lifetime),通常生命周期可以被隐式地推断出来,否则需要显式写出。生命周期的主要目标是避免悬垂引用,Rust 编译器使用借用检查器(borrow checker)比较作用域,确保所有的借用都是有效的。r 和 x 的生命周期分别被标记为 'a 和 'b,编译器会发现 r 引用的变量 x 的生命周期比自身更小,这会导致悬垂引用,所以编译器会报错。
1 2 3 4 5 6 7 8 9 10
fnmain() { // error[E0597]: `x` does not live long enough letr; // ---------+-- 'a // | { // | letx = 5; // -+-- 'b | r = &x; // | | } // -+ | // | println!("r: {r}"); // | } // ---------+
1 2 3
&i32// a reference &'ai32// a reference with an explicit lifetime &'amuti32// a mutable reference with an explicit lifetime
生命周期注解语法用于将函数参数和返回值的生命周期相关联,生命周期也是泛型。以下函数签名表示,泛型生命周期参数 'a 的具体生命周期是 x 和 y 中生命周期的较小者,返回值不能在生命周期 'a 结束之后使用。由于下面的 result 在 y 生命周期结束之后访问,所以会引发编译错误。
1 2 3
fnlongest<'a>(x: &'astr, y: &'astr) -> &'astr { if x.len() > y.len() { x } else { y } }
1 2 3 4 5 6 7 8 9
fnmain() { letstring1 = String::from("long string is long"); letresult; { letstring2 = String::from("xyz"); result = longest(string1.as_str(), string2.as_str()); // error[E0597]: `string2` does not live long enough } println!("The longest string is {result}"); }
结构体可以存储引用类型,不过此时需要使用生命周期注解。下面注解意味着,ImportantExcerpt 实例不能在其字段 part 的生命周期结束之后使用。
>>> # 3 times 'un', followed by 'ium' >>> 3 * 'un' + 'ium' 'unununium' >>> 'Py''thon' 'Python' >>> text = ('Put several strings within parentheses ' ... 'to have them joined together.') >>> text 'Put several strings within parentheses to have them joined together.' >>> word = 'Python' >>> word[42] Traceback (most recent call last): File "<stdin>", line 1, in <module> IndexError: string index out of range >>> word[4:42] 'on' >>> word[0] = 'J' Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'str'object does not support item assignment >>> 'J' + word[1:] 'Jython' >>> len(word) 6
>>> a = ['Mary', 'had', 'a', 'little', 'lamb'] >>> for i inrange(len(a)): ... print(i, a[i]) ... 0 Mary 1 had 2 a 3 little 4 lamb
for 和 while 循环可以有对应的 else 子句,如果循环在未执行 break 的情况下结束,则会执行该 else 子句。pass 语句不执行任何操作,当语法上需要一个语句,而程序不需要执行任何操作时,可以使用该语句。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
>>> for n inrange(2, 10): ... for x inrange(2, n): ... if n % x == 0: ... print(n, 'equals', x, '*', n//x) ... break ... else: ... # loop fell through without finding a factor ... print(n, 'is a prime number') ... 2is a prime number 3is a prime number 4 equals 2 * 2 5is a prime number 6 equals 2 * 3 7is a prime number 8 equals 2 * 4 9 equals 3 * 3
1 2 3
>>> whileTrue: ... pass# Busy-wait for keyboard interrupt (Ctrl+C) ...
match 语句用于模式匹配,使用 _ 通配符匹配剩余情况,使用 | 将多个字面值组合到一个模式中。模式之后可以使用 if 子句,如果判断结果为假,则 match 会继续尝试匹配下一个 case 块。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
classPoint: __match_args__ = ('x', 'y') def__init__(self, x, y): self.x = x self.y = y
match points: case []: print("No points") case [Point(0, 0)]: print("The origin") case [Point(x, y)]: print(f"Single point {x}, {y}") case [Point(0, y1), Point(0, y2)]: print(f"Two on the Y axis at {y1}, {y2}") case _: print("Something else")
1 2 3 4 5
match point: case Point(x, y) if x == y: print(f"Y=X at {x}") case Point(x, y): print(f"Not on the diagonal")
>>> deffib(n): # write Fibonacci series less than n ... """Print a Fibonacci series less than n.""" ... a, b = 0, 1 ... while a < n: ... print(a, end=' ') ... a, b = b, a+b ... print() ... ... # Now call the function we just defined: ... fib(2000) ... 011235813213455891442333776109871597
>>> deff(a, L=None): ... if L isNone: ... L = [] ... L.append(a) ... return L ... ... print(f(1)) ... print(f(2)) ... print(f(3)) ... [1] [2] [3]
可以使用 *args 表示可变参数,该参数是元组类型。可以使用 * 和 ** 执行解包。
1 2 3 4 5
>>> list(range(3, 6)) # normal call with separate arguments [3, 4, 5] >>> args = [3, 6] >>> list(range(*args)) # call with arguments unpacked from a list [3, 4, 5]
1 2 3 4 5 6 7 8 9
>>> defparrot(voltage, state='a stiff', action='voom'): ... print("-- This parrot wouldn't", action, end=' ') ... print("if you put", voltage, "volts through it.", end=' ') ... print("E's", state, "!") ... ... d = {"voltage": "four million", "state": "bleedin' demised", "action": "VOOM"} ... parrot(**d) ... -- This parrot wouldn't VOOM if you put four million volts through it. E's bleedin' demised !
列表推导式的方括号 [] 中包含以下内容:一个表达式,一个 for 子句,之后是零个或多个 for/if 子句。使用 zip() 函数在多个迭代器上并行迭代,每个迭代器返回一个元素组成元组。使用 del 语句删除列表元素。
1 2 3 4 5 6 7 8 9
>>> combs = [] ... for x in [1,2,3]: ... for y in [3,1,4]: ... if x != y: ... combs.append((x, y)) ... ... combs ... [(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
1 2
>>> [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y] [(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
>>> basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'} >>> print(basket) # show that duplicates have been removed {'orange', 'banana', 'pear', 'apple'} >>> 'orange'in basket # fast membership testing True >>> 'crabgrass'in basket False
>>> # Demonstrate set operations on unique letters from two words >>> >>> a = set('abracadabra') >>> b = set('alacazam') >>> a # unique letters in a {'a', 'r', 'b', 'c', 'd'} >>> a - b # letters in a but not in b {'r', 'd', 'b'} >>> a | b # letters in a or b or both {'a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'} >>> a & b # letters in both a and b {'a', 'c'} >>> a ^ b # letters in a or b but not both {'r', 'd', 'b', 'm', 'z', 'l'}
字典的键必须是不可变类型,使用 {} 创建空字典,获取不存在键的值会报错。使用 list(d) 获取键列表,使用 sorted(d) 获取排序之后的键列表,使用 in 或 not in 判断键是否存在,使用 dict() 函数创建字典。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
>>> tel = {'jack': 4098, 'sape': 4139} >>> tel['guido'] = 4127 >>> tel {'jack': 4098, 'sape': 4139, 'guido': 4127} >>> tel['jack'] 4098 >>> del tel['sape'] >>> tel['irv'] = 4127 >>> tel {'jack': 4098, 'guido': 4127, 'irv': 4127} >>> list(tel) ['jack', 'guido', 'irv'] >>> sorted(tel) ['guido', 'irv', 'jack'] >>> 'guido'in tel True >>> 'jack'notin tel False
>>> knights = {'gallahad': 'the pure', 'robin': 'the brave'} ... for k, v in knights.items(): ... print(k, v) ... gallahad the pure robin the brave
1 2 3 4 5 6
>>> for i, v inenumerate(['tic', 'tac', 'toe']): ... print(i, v) ... 0 tic 1 tac 2 toe
1 2 3 4 5 6 7 8
>>> questions = ['name', 'quest', 'favorite color'] ... answers = ['lancelot', 'the holy grail', 'blue'] ... for q, a inzip(questions, answers): ... print('What is your {0}? It is {1}.'.format(q, a)) ... What is your name? It is lancelot. What is your quest? It is the holy grail. What is your favorite color? It is blue.
使用 is 或 is not 比较两个对象是否是同一个对象。比较运算支持链式操作,a < b == c 和 a < b and b == c 等价。and、or 和 not 表示与或非,相当于 Java 中的 &&、|| 和 !。布尔值有 True 和 False,使用大写字母开头。在表达式内部赋值需要使用 :=,避免误用 =。布尔运算中使用普通值而不是布尔值时,短路运算的返回值是最后一个已求值的参数。
1 2 3 4
>>> string1, string2, string3 = '', 'Trondheim', 'Hammer Dance' >>> non_null = string1 or string2 or string3 >>> non_null 'Trondheim'
kind = 'canine'# class variable shared by all instances
def__init__(self, name): self.name = name # instance variable unique to each instance
>>> d = Dog('Fido') >>> e = Dog('Buddy') >>> d.kind # shared by all dogs 'canine' >>> e.kind # shared by all dogs 'canine' >>> d.name # unique to d 'Fido' >>> e.name # unique to e 'Buddy'
defupdate(self, iterable): for item in iterable: self.items_list.append(item)
__update = update # private copy of original update() method
classMappingSubclass(Mapping):
defupdate(self, keys, values): # provides new signature for update() # but does not break __init__() for item inzip(keys, values): self.items_list.append(item)