Rust_note01

  • 最近,在学习rust,本来想快速入门一下rust,然后补一下操作系统的知识,就感觉去写一个开源操作系统,以后拿来装装b….但这都只是幻想,在跟着语言圣经学习的过程中,发现rust和之前接触过的所有语言都不一样,之前不管是python,java还是go,学起来都没什么太大的难度,至少入门没什么问题,但是,rust真的是一门比较难的语言,从它的语言上来看就非常的不好理解。老实说,虽然作者说cpp比rust难,但我真的觉得rust比cpp难多了(也有可能是我cpp学的太烂了….bushi)。所以来简单复盘一下,这还是我第一次学一门语言,专门复盘。
  • 所有权

    • 这是第一个,我觉得和其他语言非常不一样的点。熟悉java的同学都知道,java有自己独有的一套垃圾回收的机制,几乎所有的多余操作,idea都帮你解决,只要专注在代码上就ok,还有内存的分配和释放,虽然java等其他语言也有这样的操作,比如
    • 1
      2
      3
      4
      5
      public static void main(String arg[]){
      OutputStream out = new FileOutputStream("file");
      // Some special code
      out.close();
      }
    • 但其实对程序员来说,并没有什么实际的影响,对于大部分非科班出身的程序员更是只把这个当作结构化的代码,诸如此类的代码,在rust中会被颠覆!
    • 首先,引用大佬的C语言案例
    • 1
      2
      3
      4
      5
      6
      int* foo() {
      int a; // 变量a的作用域开始
      a = 100;
      char *c = "xyz"; // 变量c的作用域开始
      return &a;
      } // 变量a和c的作用域结束
    • 这段代码如果放在main函数中,或许还能勉强说的过去,但是这是一个自定义函数,局部变量a和c都被存放在了栈上的缓冲区,熟悉pwn的同学应该知道,这个很容易造成bufferoverflow(“栈溢出”)也就是常说的造成”内存安全”,所以rust就舍弃这样的方法。
    • 在rust中,所有的值和变量都保持这一一对应的关系,当定义一个变量,并且给它赋值,那么这个值的所有权就在这个变量手上,如果想要调用这个变量,只能通过借用的方法,比如
    • 1
      2
      3
      4
      5
      fn main() {
      let s = String::from("hello");
      s.push_str(", world!");
      println!("{}" , s);
      }
    • 上面的代码是rust的形式,可以很容易看出来,如果把里面特有的rust的函数换成javascript函数,运行这个函数没有任何问题,但是因为这个是rust,所以这段代码会直接编译不通过,那么来解释一下为什么会编译不通过。
    • 就像上面说的,每一个值都对应它初始化的那个变量,比方说字符串”hello”,一开始,它的所有权就在s身上,如果不做任何标注的话,只能通过引用来访问变量s里面的值,并且不可以修改值,就像java中如果一个类被import到main函数主类,java里的private和protected变量以及方法默认都是无法被访问的,只能通过间接的公共函数来获取或修改值。那么在rust里也有这样一个”中间人”就是”引用” ‘&’。
    • 先来看一下所有权的一些小问题吧
    • 1
      2
      3
      4
      5
      fn main() {
      let s1 = String::from("hello");
      let s2 = s1;
      println!("{} , {}" , s1 , s2);
      }
    • 还是那句话,如果在javascript里,这样的代码运行不会有任何错误
    • image.png
    • 可以看到,完美输出,但是在rust里呢?
    • image.png
    • 可以看到,非常果断的报错噢,就像她当时拒绝你一样果断….把你挡在rust的大门外,呜呜呜…..
    • 开玩笑,简单看一下为啥报错,
    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      error[E0382]: borrow of moved value: `s1`
      --> src/main.rs:4:24
      |
      2 | let s1 = String::from("hello");
      | -- move occurs because `s1` has type `String`, which does not implement the `Copy` trait
      -- 这里的大概意思说是因为s1这个字符串没有实现Copy特征
      3 | let s2 = s1;
      | -- value moved here
      -- 意思就是变量的所有权被转移到了s2
      4 | println!("{} , {}" , s1 , s2);
      | ^^ value borrowed here after move
    • 首先来解决第一个提示,什么叫实现特征?
    • 大概和接口的意思比较像,一个特征就像一个接口,里面来写哪些方法需要去实现。为什么说rust很快,性能很好,就是因为和c语言非常像,是在栈和堆上进行操作的。rust里大概有基础的数据类型和一些…非基础的数据类型,比如基础类型有 i32 , u32 , i64 ,u64这些都是整形,还有str , bool,这些都占4个字节,全部存放在栈上,并且基础的类型都实现copy特征,比如
    • 1
      2
      3
      4
      5
      fn main() {
      let x = 1;
      let y = x;
      println!("{} , {}" , x , y);
      }
    • image.png
    • 可以看到程序正常编译运行,它可以正常运行的原因就是1是一个整形,那么它实现了Copy方法,在进行像上面这样的情况的赋值的时候,它交出的不是所有权,而是一个它的clone体,相当于把栈上x的4个字节的区域复制一份再压入栈。而之前的s1的定义方法可以看到String::…(“hello”),这样定义的字符串是String类型的,它和4个字节大小的str类型不一样,String类型是存在堆上的,所以它没有实现Copy方法,也就导致了那个编译器的错误。
    • 第二个提示,说是value moved here,说是值被移动到这,其实就是”hello”这个值的所有权被移动到了,s2这里,s1失去了对”hello”这个字符串的所有权,所以就会直接被drop掉,那么这个变量在整个作用域,就不能再被使用,也就当然不能被输出。
    • 所以当代码被改成这样
    • 1
      2
      3
      4
      5
      fn main() {
      let s1 = String::from("hello");
      let s2 = s1;
      println!("{} " , s2);
      }
    • image.png
    • 也就可以被正常编译执行了。
  • 引用

    • 引用也是一个我觉得非常新颖的概念,在以前的coding中从来没有遇到过这样的情况,或者类似的概念,直接上代码
    • 1
      2
      3
      4
      5
      6
      fn main() {
      let x = 5;
      let y = &x;

      println!("{} , {}" , x , y);
      }
    • 先来看运行结果
    • image.png
    • 可以看到,非常完美的执行了,在rust中符号”&”就代表引用的意思,let y = &x;的意思就是y来引用x,这样就不会导致所有权的转移,也就不会发生悬垂引用的错误。
    • 不可变引用

      • 引用主要分为可变引用和不可变引用,在上面的代码中,y的定义的方法就是不可变引用,意思就是y只能访问x的值但是不能修改
      • 1
        2
        3
        4
        5
        6
        7
        fn main() {
        let x = 5;
        let y = &x;

        y = 10;
        println!("{} , {}" , x , y);
        }
      • image.png
      • 可以看到报错了,报错信息是
      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        error[E0308]: mismatched types
        --> src/main.rs:5:7
        |
        3 | let y = &x;
        | -- expected due to this value
        4 |
        5 | y = 10;
        | ^^ expected `&{integer}`, found integer
        |
        3help: consider borrowing here
        |
        5 | y = &10;
      • 意思就是不能修改引用的值,这是一个非常安全的做法,就和在java中外部类不能访问导入类的private和protected属性。
      • 可变引用

      • 在rust里当然也可以使用可变引用来对变量进行修改,
      • 1
        2
        3
        4
        5
        6
        fn main() {
        let mut x = String::from("hello");
        let y = &mut x;
        y.push_str(" , world!");
        println!("{} , {}" , x , y);
        }
      • image.png
      • 可以看到编译器还是无情的把程序crash掉,说明还是有问题,其实还是所有权的问题,因为改变了x变量,所以最后改变之后,string的所有权还是到了y身上。但其实还有种办法就是利用函数来进行可变引用的修改
      • 1
        2
        3
        4
        5
        6
        7
        8
        fn main() {
        let mut s = String::from("hello");
        change(&mut s);
        println!("{}" , s);
        }
        fn change(some_string: &mut String) {
        some_string.push_Str(", world~");
        }
      • image.png
      • 这大概就是引用的部分,和c语言的取地址符号很像,所以对应的还有一个解引用符号”*”。
      • 1
        2
        3
        4
        5
        6
        7
        fn main() {
        let x = 5;
        let y = &x; // 实际上&x也相当于是一个地址的符号,指向的并不是x真实的值,而是x真实值所对应的地址

        assert_eq!(x , 5);
        assert_eq!(5, *y); 所以如果需要断言的话,就应该是先*(&x)解引用
        }
      • image.png
      • 可以看到很正确,没有任何错误输出,如果是没有解引用
      • image.png
      • 恭喜,又crash了!

Rust_note01
http://st3r665.github.io/2025/07/15/Rust-note01/
作者
St3r
发布于
2025年7月15日
许可协议