Rust语言学习记录

本文介绍了Rust编程语言的基础,包括安装、HelloWorld示例、Cargo工具的使用、猜数游戏项目、变量与可变性、数据类型(整数、浮点、字符)、复合类型(元组和数组)、函数、控制流(if、循环)、所有权系统、引用与借用、切片、struct和枚举的使用,以及常见的heap存储结构如vector和String。
摘要由CSDN通过智能技术生成

参考教材  --》 Rust权威指南

一、入门

        1.安装Rust

        2.helloworld

fn main(){
    println!("helloworld"); // 通常以 ;结尾  
}

          3.Cargo

                 |_____cargo.toml 配置文件格式

                 |_____src  存放源代码

                 cargo build -创建可执行文件

                 cargo run -构建并运行可执行文件

                 cargo check 检查代码确保能通过编译, 但是不产生任何可执行文件

二、猜数游戏

项目描述:随机生成1-100的数与用户输入的数进行比较并给用户提示

use std::cmp::Ordering;
use std::io; 
use rand::Rng; // trait
// prelude 预导入 
fn main(){
    println!("猜数");
    let secret_number = rand::thread_rng().get_range(1, 101);
    pringln!(" input  a  number");
    // 使用 let 声明变量  如果没有mut 则表明该变量不可变
    // :: new()关联函数
    loop{ // 循环
    let mut guess = String :: new();
    // io::result 如果是err expect会中断当前运行
    io::stdin().read_line(&mut guess).expect("无法读取");
    let guess:u32 = guess.trim().parse().expect("please enter a number");
    println!("number is {}", guess);
    match guess.cmp(&secret_number){
        Ordering::Less=>println!("too small"),
        Ordering::Equal=>{println!("too Equal"); break}, // 相等时跳出循环
        Ordering::Greater=>println!("too big"),
        }
    }
}

三、通用的变成概念

3.1变量与可变性 

                声明变量使用let关键字。默认是不可变的。如果想更改变量的字可以使用mut关键字

                常量:绑定值以后也是不可变的 与不可变的变量的区别

                                不可以使用mut  常量永远都是不可变的

                                声明常量使用const关键字,他的类型必须被标注

                                常量可以再任何作用域内进行声明 包括全局作用域

                                 常量只可以绑定到常量表达式,无法绑定到函数的调用结果或只能在运行时才能计算出的值

3.1.2 shadowing

                 可以使用相同的名字声明新的变量  新的变量就会shadow之前声明的同名变量

fn main(){
    let x = 1;
    let x = x + 1;
    // 之后的x就变成了第二次声明的x的值  我们可以输出验证一下
    println!("{}",x);
    // 现在的x的类型是 u32 如果我们执行下面一段代码后x的类型会变成什么呢?
    let x = "AAAA";
    // 是的,是string
    // 也就是说在Rust 中我们可以通过shadowing来突破类型的限制 但是下面的一段代码的执行                
    // 结果呢?
    let mut xa = "    ";
    xa = xa.len(); 
    
}

3.2数据类型

        标量和复合类型

                Rust是静态编译语言,在编译时必须知道所有变量的类型

                基于使用的值,编译器通常可以推断出他的具体类型  

        整数类型  默认是i32

                无符号整数

                有符号整数

        浮点类型 f32 f64(默认类型)

        数值操作 + - * / % 

        布尔类型  true \ false

        字符类型  char   四字节  使用单引号

3.3复合类型

        复合类型可以将多个值放在一个类型里

        Rust提供了两种基础的复合类型元组(Tuple)、数组 

        Tuple 可以放入多个不同类型的值 但是长度是固定的 

fn main(){
    let tup: (i32, f64, u8) = (1,2.0,3);
    let (x,y,z) = (1,2.0,3);
    println!("{},{},{}",x,y,z);
    let x = tup;
    println!("{}",x.0)
}

        数组:相同类型的集合  数组的长度是固定的

        数组的类型以这种形式表示: [ 类型;长度 ]

        如果数组的每个元素值都相同  例如: let a = [3;5] => let a = [3,3,3,3,3]

let a = [3;5];
println!("{}",a[1]);

3.4函数

        声明函数使用fn关键字  必须指明参数的类型

        函数的返回值

                在->符号后边声明函数返回值的类型,但是不可以为返回值命名

                在Rust里面,返回值就是函数体里面最后一个表达式(简单来说就是没有分号)的值

                也可以使用return 进行返回

fn plus(x:i32,y:i32)->i32{
    x + y;
    return x + y;

    // 与  x + y 等价
}

3.5 if 表达式 条件必须是bool类型 if 与else 中的返回值的类型必须一样的类型 

3.6循环

        loop   重复执行作用域的代码 类似于 while true  

        while条件循环  每次执行循环体之前都要判断一次条件  

        for

    let a = [1,2,3,4,5,6];
    for element in a.iter(){
        println!("the value is :{}", element)
    }

        Range 制定一个开始和一个结束    不包含结束   rev可以反转range

    for number in (1..4).rev(){
        println!("{}",number)
    }

四、所有权

        Rust的核心特性就是所有权

        所有程序在运行时都必须管理它们使用计算机内存的方式

                有些语言有垃圾收集机制,在程序运行时,它们会不断地寻找不再使用的内存

                在其他语言中,程序员必须显式地分配和释放内存 

        Rust采用了第三种方式

                内存是通过一个所有权系统来管理的,其中包含一组编译器在编译时检查的规则。

                当程序运行时,所有权特性不会减慢程序的运行速度

栈内存:按值的接收顺序来存储,按相反的顺序将他们移除

                -添加数据叫做压入栈

                -移除数据叫做弹出栈

                所有存储在栈伤的数据必须拥有已知的固定的大小

                        编译时大小未知的数据或运行时大小可能发生变化的数据必须存在堆上

堆内存:当你把数据放入堆上,你会请求一定数量的空间

                操作系统在堆上找到一块足够大的空间,把他标记为在用,并返回一个指针,也就是这个空间的地址

                这个过程叫做在堆上进行分配

        指针是已知固定大小可以把指针存在栈上

        访问堆上的数据要比访问栈上的数据慢因为需要通过指针才能找到堆上的数据

        所有权解决的问题

                跟踪代码的哪些部分正在使用堆上的哪些数据

                最小化堆上的重复数据量

                清理堆上未使用的数据以避免空间不足

所有权规则

        每个值都有一个变量,这个变量是该值的所有者

        每个值同时只能有一个所有者

        当所有者超出作用域(scope)时,该值将被删除

stack上的数据:复制

        Copy trait 可以用于像整数这样完全存放在stack上面的数据

        如果一个类型实现了Copy这个trait,那么旧的变量在赋值后仍然可用

        如果一个类型或者该类型的一部分实现了Drop trait 那么Rust不允许让在再去实现Copy trait         一些拥有Copy trait的类型

        任何简单标量的组合类型都可以是Copy的

        任何需要分配内存或某种资源的都不是Copy的

         一些拥有Copy的类型

                所有的整数类型   bool   char   所有的浮点类型

                Tuple(如果其所有元素是可以copy的)

所有权与函数

        

use std::collections::btree_map::Range;

fn main() {
    let s = String::from("hello world");
    take_ownership(s);
    // println!("s:{}",s);   报错

    let x = 5;
    makes_copy(x);
    println!("X:{}",x);
}

fn take_ownership(some_string: String){
    println!("some_string:{}",some_string);
}

fn makes_copy(some_number: i32){
    println!("some_number:{}",some_number);

}

返回值 与 作用域

        函数在返回值的过程中同样也会发生所有权的转移

        一个变量的所有权总是遵循同样的模式:

                把一个值赋给其他变量时就会发生移动

                当一个包含堆数据的变量离开作用域时,他的值就会被drop函数清理,除非数据的所有权移动到另一个变量上了。

4.2 引用与借用

fn main(){
    let s1 = String :: from ("hello");
    let len = calculate_length(&s1);
    println!("The length of '{}' is {}.", s1,len);
}

fn calculate_length(s : &String) -> usize{
    s.len()
}

        参数的类型是&String 而不是 String

        &符号表示引用,允许你引用某些值而不取得其所有权

借用: 把引用作为函数参数这个行为叫做借用

            不可修改借用的东西

            可变引用

fn main(){
    let mut s1 = String :: from ("hello");
    let len = calculate_length(&mut s1);
    println!("The length of '{}' is {}.", s1,len);
}

fn calculate_length(s : &mut String) -> usize{
    s.push_str(" world");
    s.len()
}

可变引用有一个重要的限制: 在特定作用域内,对某一块数据,只能有一个可变的引用

                -这样做的目的是为了防止在编译时发生数据竞争

                以下三种行为会发生数据竞争

                两个或多个指针同时访问同一个数据

                至少有一个指针写入数据

                没有使用任何机制来同步数据的访问 

        不可以同时有可变引用和不可变引用 但是可以有多个不可变引用

4.3切片

        Rust的另外一种不持有所有权的数据类型

        字符串切片是指向字符串中一部分内容的引用

        形式:[开始索引..结束索引]

        开始索引就是切片开始位置的索引值

        结束索引是切片终止位置的下一个索引值

五、struct

        5.1定义并实例化struct

                struct结构体 自定义的数据类型

                定义struct       

                使用struct关键字,并为整个struct命名

                在花括号内,为所有字段(Field)定义名称和类型

                实例化struct\

                 取得struct里面的某个值:使用点标记法

                struct更新语法

                        基于某个struct实例创建一个新的实例的时候可以使用更新语法

 let user1 = User{
        email: String::from("abc@126.com"),
        username: String::from("nikky"),
        active: true,
        sign_in_count: 556,
    };
    let user2 = User{
      email: String::from("AAAA@qq.com"),
        ..user1
    };

Tuple Struct 可定义类似tuple的struct 叫做tuple struct

        Tuple struct 整体有名,但是里面的元素没有名

        适用:想给整个Tuple起名并让他不同于其他tuple而且又不需要给每个元素起名

        定义Tuplestruct:使用struct关键字,后边是名字,以及里面元素的类型

                例如:struct color(i32,i32,i32);

5.3 struct的方法

        方法和函数类似: fn关键字、名称、参数、返回值

        方法与函数不同之处:

                方法是在struct的上下文中定义

                第一个参数是self 表示方法被调用的struct实例

语法:

        impl 结构体名{

                fn 方法名(参数)-> 返回值{

                        代码        

                }

        }

Rust会自动应用或解引用

关联函数:第一个参数不是self

通过结构体名 :: 符号进行调用

六、枚举与模式匹配

6.1 定义枚举

6.2 Option 枚举

        定义与标准库中

        在预导入模块中 

        描述了某个值可能存在或不存在的情况

        enum Option<T>{

                Some(T),

                None,

        }

        若想使用Option<T>中的T 必须将他转换成T

6.3match匹配

        允许一个值与一系列模式进行匹配,并执行匹配的代码

        使用match必须穷举所有可能性

        可以根据实际需求 对其他情况使用_

6.4 if let 

        if let 只针对一种模式匹配

八、常用的集合(存储在heap伤)

        8.1 vector 

                Vec<T>叫做 vector

                        由标准库提供

                        可存储多个值

                        只能存储相同类型的数据

                        值在内存中连续存放

                创建Vector

                        Vec::new函数

                        使用初始值创建Vec<T> 使用vec!宏

                更新Vector

                        向Vector添加元素使用 push方法

                删除Vector

                        与任何struct一样 当Vector离开作用域后

                                他就被清理掉了

                                他所有的元素也被清理掉了

                读取Vector元素

                                索引      get方法 

fn main(){
    let mut v : Vec<i32> = Vec::new(); // 使用了泛型
    // let v = vec![1,2,3];
    v.push(11);
    v.push(12);
    v.push(12);
    v.push(12);
    v.push(12);
    // 使用索引方法读取下标元素的值可能会引发异常
    let third: &i32 = &v[2];
    println!("The Third element is {}", third);
    // 使用match不会
    match v.get(2){
        Some(third) => println!("The Third element is {}", third),
        None => println!("no element"),
    }
    for i in v {
        println!("{}",i);
    }
}

        8.2 String

                String类型

                        来自标准库而不是核心语言

                        可增长可修改可拥有

                创建一个新的字符串

                        String::new()

                        也可以使用初始值来创建字符串

                                to_string()方法

                                String::from() 函数,从字面值创建String

                        更新String

                                push_str()方法 把一个字符串切片添加 不会获得所有权

                                push() 添加一个字符

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值