Loading

Rust学习-阶段1学习总结

  学习Rust已经两周了,基本上是断断续续的在学,或者是在上下班坐公交时,或者是在ODC没事做时。现在已经学习了Rust程序设计语言的前5章,是时候做一个总结了。关于数据类型或者if else这种内容我就不在罗列了,我只说一说我感兴趣的部分。

  • 变量和可变性(variable & mutability)
  •  函数的隐式返回值
  •  理解语句(statement)和表达式(expression)的区别
  •  break可以带变量
  • 元组
  • 所有权(ownership)

 其中所有权我会重点说一下。

    先看看Rust官网的slogan,slogan往往是可以有一个更加整体的理解。
    
 

 

   我抽取下Key words,1. 每个人  2. 可靠且高效。“每个人”说明这门语言不会像面向machine的汇编或者C,应该具备现代语言的一些特性,泛型,反射,完善的函数库,好用的集合等等。可靠且高效,“可靠”就是说写出来的程序的漏洞会少,不会像C那样不检查下标越界,也不会产生一些常见的内存错误(doule free或者悬挂指针之类的)。当说到“高效”我想到了C++,大家都说C++的编译时间长且开发很慢(c++我没什么发言权,说的不对,诸君还请谅解),既然是高效,我想就不能仅仅是运行时间快了吧,开发时间也应该快。下面我们探讨一下我上面说的几点。

  

1. 变量和可变性

  rust中变量默认是不可变的,也就是说下面的类似下面的赋值操作是非法的。

fn main() {
    let a = 100;
    a = 101;
}

  会出现这样的错误,“不能给一个不可变的变量赋值两次。”

 

  如果你需要一个“会变”的变量,那你需要用 mut 关键字去显式(explicitly)的声明一个变量。这么设计的原因,为什么我们需要显示的去声明一个变量为可变的?我也不知道,我猜测跟所有权有关?

fn main() {
    let mut a = 100;
    a = 101;
}

 

2.  函数的隐式返回值

      如果函数不适用return语句进行返回的话,函数隐式的返回最后一个表达式。当然这种写法是可以选装的,你可以选择传统的return版的返回语句。

fn add_one(num: i32)->i32 {
    num+1
}

fn main() {
    let mut a = 100;
    a = 101;
    println!("{}", add_one(a));
}

 

3. 理解语句(statement)和表达式(expression)的区别

  rust对于语句和表达式的区分比较严格,语句就是执行一些操作但并不产生结果的指令,表达式计算并产生一些值。

let mut num = 100;  //一个赋值语句
num = num + 1;  //语句
num + 1   //表达式

   函数调用是一个表达式。宏调用是一个表达式。我们用来创建新作用域的大括号(代码块),{},也是一个表达式。

let y = {
    let x = 3;
    x+1
};

 

4.  break可以带变量

  就下边那样,loop是rust中的循环,类似while(1)。  

fn main() {
    let mut counter = 0;
    let result = loop {
        counter += 1;
        if counter == 10 {
            break counter*2;
        }
    };
    println!("The Result is {}", result);
}

 

5.  元组

  元组(tuple)类型是一个复合类型,它可以包含多种不同类型。这种类型的形式是 " (type1, type2, type3 ... ... ) "。

let tuple1 = (1, "hello", 3.14);
let tuple2: (i32, string, f32) = (1, "hello", 3.14);

  那么怎么去获取元组的元素呢。可以通过元组的解构(destructuring),通过解构元组我们可以把元组内的值赋给其他的变量。

let (x, y, z) = tuple1;

  也可以使用点号(".")去直接访问元组内的元素

let x = tuple1.0;
let y = tuple1.1;
let z = tuple1.2;

 

6.  所有权(ownership)

  所有权是rust一个特点之一,也是rust内存管理的核心。每个值(可以理解是内存的一小块)都有一个所有者,这个所有者可以是变量。同时程序超出所有者的作用域时,所有者所有的值也会通过调用drop()函数,从而被释放。(注意drop不是通过运行时来调用的,而是在编译期就被插入了drop函数),这种做法类似于C++中的RAII机制。值得注意的是,所有权是可以被转移的,并且所有者在转移了值的所有权之后,他就会被rust认为是无效的所有者,再次使用该变量(所有者)会产生错误。

fn foo(str_cp:String) {
    println!("{}", str_cp);    
}

fn main() {
    let str1 = String::from("hello world");  
    foo(str1);                              
    println!("{}", str1);                
}

  在上边的例子中,我们在堆上创建了一个字符串"hello world",该字符串的所有者是变量str1,值得注意的是,将变量作为函数的形参也会转移值的所有权,因此我们当7行的函数调用返回时,str1已经是一个无效的所有者,可怜的str1,白白丢了自己的地盘,但是他也不是什么都没有收获的,这种所有权的转移,也是有好处的。

 

 

  这里我生搬硬套了一种情况,尝试说明这种所有权转移的机制是如何防止double free的情况产生的。下面是一段和上述代码段类似的代码段,可以看到这里对同一内存块进行了两次free。那么rust是如何防止这种情况产生的呢?实际上在rust中17行的情况根本就不会出现,因为str1在此时已经是无效的了,没有free它的可能性。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void foo(char *str) 
{
    printf("%s\n", str);
    free(str);
}

int main(void) 
{
    while (1) {
        char *str1 = (char *)malloc(512);
        strncpy(str1, "hello world", 512);
        foo(str1);
        free(str1);  //double free
        printf("%s\n", str);
    }
    return 0;
}

  就这样,本文只是我对前几天的学习进行的一个简单的总结。很多重要的东西都没有覆盖,等我的rust用的更熟练,我会尝试出一个Rust教程。Peace。

参考  

 

posted @ 2019-12-22 22:50  成蹊0xc000  阅读(496)  评论(2编辑  收藏  举报