rust与python/c++语法区别

强烈推荐此书入门rust:https://course.rs/basic/intro.html

其实想想,在长大之后,我们感激人生路上遇到过的严师益友,正是因为他们,我们才在正确的道路上不断前行,虽然在那个时候,并不能理解他们,而 Rust 就如那个良师益友,它不断的在纠正我们不好的编程习惯,直到某一天,你发现自己能写出一次性通过的漂亮代码时,就能明白它的良苦用心。

符号表

解释 例子
! println!();
? try!宏 let v = xxx()?; xxx()?.yyy()?;
' 标签,方便编译器
* 解引用deref
& 引用
: a:b,则a⊇b item:i32item: &(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(())
}
posted @ 2024-02-28 17:37  Nolca  阅读(24)  评论(0编辑  收藏  举报