rust与python/c++语法区别
强烈推荐此书入门rust:https://course.rs/basic/intro.html
其实想想,在长大之后,我们感激人生路上遇到过的严师益友,正是因为他们,我们才在正确的道路上不断前行,虽然在那个时候,并不能理解他们,而 Rust 就如那个良师益友,它不断的在纠正我们不好的编程习惯,直到某一天,你发现自己能写出一次性通过的漂亮代码时,就能明白它的良苦用心。
符号表
解释 | 例子 | |
---|---|---|
! | 宏 | println!(); |
? | try!宏 | let v = xxx()?; xxx()?.yyy()?; |
' | 标签,方便编译器 | |
* | 解引用deref | |
& | 引用 | |
: | 若a:b ,则a⊇b |
如item:i32 、item: &(impl Summary + Display) 、pub fn notify<T: Summary + Display>(item: &T) {} 、'a: 'b |
trait MyTrait {}
struct MyStruct<'a,T> {
part: &'a T,
}
//'a: 'b,是生命周期约束语法,很像泛型约束,用于说明 'a 至少比 'b 活得久
impl<'a: 'b, 'b,T> MyTrait for MyStruct<'a,T> {}
//或者用where
impl<'a,T> MyTrait for MyStruct<'a,T> {
fn MyFunc<'b>(&'a self, msg: &'b str) -> &'b str
where 'a: 'b,
{
println!("MSG: {}", msg);
self.part
}
}
enum vs struct
enum多选一
struct全都有
if / match
生命周期误解:https://zhuanlan.zhihu.com/p/165976086
生命周期标注:https://www.runoob.com/rust/rust-lifetime.html
pub fn animal_habitat(animal: &str) -> &'static str { //&'static vs &mut, 详见lifetime标注
let id = if animal == "crab" { //id等于(或拥有)了一个匿名函数的返回值
1
} else if animal == "gopher" {
2
} else if animal == "snake" {
3
} else {
-1
};
}
match number {
// 匹配单个值
1 => println!("One!"),
// 匹配多个值
2 | 3 | 5 | 7 | 11 => println!("This is a prime"),
// 匹配一个闭区间范围
13..=19 => println!("A teen"),
// else
_ => println!("Ain't special"),
}
matches! / if let / while let 语法糖
v.iter().filter(|x| matches!(x, MyEnum::Foo));
let v = Some(3u8);
match v {
Some(3) => println!("three"),
_ => (),
}
if let Some(3) = v {
println!("three");
}
区别?
if Some(3) == v{
println("three");
}
if let 可以用于模式匹配和解构,而 == 只是简单的比较。在这个特定的例子中,因为我们只是在比较 v 是否等于 Some(3),所以两种方式的效果是相同的。但是,如果我们需要解构 v 的值,那么 if let 就会更有用。例如,如果我们想要获取 v 中 Some 的值,那么我们可以写 if let Some(x) = v,然后在 if 语句的块中使用 x。
while let Some(top) = stack.pop() {
println!("{}", top);
}
let v = vec!['a', 'b', 'c'];
for (index, value) in v.iter().enumerate() {
println!("{} is at index {}", value, index);
}
for while loop
for n in 1..101 {}
for n in 1..=100 {}
for number in (1..101).rev() {}
for name in names.iter() {}
for name in names.into_iter() {}
for name in names.iter_mut() {}
'counting_up: loop {
loop {
if id == 1 {
break;
}
if id == 2 {
break 'counting_up;
}
}
slice array tuple
fn slice_out_of_array() {
let mut a :[i32] = [0; 100]; //100个0,[i32]表i32的数组
let a = [1, 2, 3, 4, 5]; //遮蔽shadow,变量另用
let nice_slice = &a[1..4];
//slice前面要加&
}
fn indexing_tuple() {
let numbers = (1, 2, 3);
let second = numbers.1; //成员
let (a,b,c)=numbers; //拆解tuple
}
vector 解引用
数组array的空间已知,分配到内存的栈Stack
部分;
向量vector空间未知,可以随意拓展/收缩空间,分配到堆Heap
。
fn array_and_vec() -> ([i32; 4], Vec<i32>) {
let a = [10, 20, 30, 40]; // a plain array
let v = vec![10, 20, 30, 40]; //macro for vectors
(a, v)
}
fn vec_loop(mut v: Vec<i32>) -> Vec<i32> {
for element in v.iter_mut() { //element为可变引用
*element*=2 //解引用,相当于指针
}
v
// At this point, `v` should be equal to [4, 8, 12, 16, 20].
}
fn vec_map(v: &Vec<i32>) -> Vec<i32> {
v.iter().map(|element| {
element*2
}).collect()
//rust怎么这么奇怪
}
enum > struct
struct Ipv4Addr {
// --snip--
}
struct Ipv6Addr {
// --snip--
}
enum IpAddr {
V4(Ipv4Addr),
V6(Ipv6Addr),
}
无copy trait的数组初始化
闭包输入参|_i|
:https://zhuanlan.zhihu.com/p/75429819
std::array::from_fn 是一个函数,它接受一个闭包作为参数,并使用这个闭包来生成数组的每个元素。在这个例子中,闭包是 |_i| String::from("rust is good!"),它忽略了输入的 _i(表示元素的索引),并总是返回字符串 "rust is good!"。
let array: [String; 8] = std::array::from_fn(|_i| String::from("rust is good!"));
println!("{:#?}", array);
特征对象 与 智能指针
自动解引用
不能用dyn Draw
,而应用&dyn Draw
fn draw1(x: Box<dyn MyType>) {
// 由于实现了 Deref 特征,Box 智能指针会自动解引用为它所包裹的值,然后调用该值对应的类型上定义的 `draw` 方法
x.draw();
}
fn draw2(x: &dyn MyType) {
x.draw();
}
fn main() {
draw1(Box::new(x));
draw2(&y);
}
特征对象数组
一般用Vec<Box<dyn MyType>>
,不用下面的写法
就算下面代码的MyType为enum枚举类型,也不如dyn MyType
的动态类型更灵活,enum无法列举未知的可能类型。
pub struct Screen<T: MyType> {
pub components: Vec<T>,
}
impl<T> Screen<T>
where T: MyType {
pub fn run(&self) {
for component in self.components.iter() {
component.draw();
}
}
}
但是这种写法限制了 Screen 实例的 Vec
中的每个元素必须是 Button 类型或者全是 SelectBox 类型。如果只需要同质(相同类型)集合,更倾向于采用泛型+特征约束这种写法,因其实现更清晰,且性能更好(特征对象,需要在运行时从 vtable 动态查找需要调用的方法)。
在实际使用场景中,特征对象数组要比枚举数组常见很多,主要原因在于特征对象非常灵活,而编译器对枚举的限制较多,且无法动态增加类型。
强大的自适应 方法/宏
collect():接受任何可迭代的内容,并将其转换为相关的集合。你拿一个集合,在上面调用 iter ,做一堆转换,最后collect()。
println!()
x.get(0) 优于 &x[0]
可能数组越界时,使用x.get(&index)。如用户的输入。
绝对不可能越界时,可直接引用 &x[index]。小幅度优化。
输入传参:有&
引用 优于 直接传参
c.get(index)
可能发生所有权转移,而c.get(&index)
则不会
输出返回:返回所有权 优于 返回(悬垂)引用
典型的悬垂引用场景:
fn longest<'a>(x: &str, y: &str) -> &'a str {
let result = String::from("really long string");
result.as_str()
}
生命周期
'outer: loop {
println!("Entered the outer loop");
'inner: loop {
println!("Entered the inner loop");
break 'outer;
}
}
用单引号'
起头,表示这只是注释,让编译器不为难我们,但如果滥用,有可能出现悬垂引用
(空指针)。
struct ImportantExcerpt<'a> {
part: &'a str,
}
<'a>
的生命周期标注说明,结构体ImportantExcerpt
所引用的字符串 str 至少比该结构体活得更久。
怎么办?那就返回内部字符串的所有权,然后把字符串的所有权转移给调用者:
fn longest<'a>(_x: &str, _y: &str) -> String {
String::from("really long string")
}
fn main() {
let s = longest("not", "important");
}
解引用 *&
v.retain(|x| *x > 10);
有错误返回的main函数
use std::error::Error;
use std::fs::File;
fn main() -> Result<(), Box<dyn Error>> {
let f = File::open("hello.txt")?;
Ok(())
}