• RUST入门.md


    RUST入门

    Hello World

    mkdir -p projects/hello_world/
    cd projects/hello_world
    

    main.rs文件

    fn main() {
        println!("hello, world!");
    }
    

    编译和运行

    rustc main.rs
    ./main
    

    格式化:rustfmt

    Cargo

    Cargo 是 Rust 的构建系统和包管理器。

    hello

    构建如下:

    1. cargo new hello_world --bin

      --bin为默认参数,表示构建二进制程序。可选--lib表示构建库。

      nsfoxer@nsfoxer-pc ~/temp> cargo new hello_world --bin
           Created binary (application) `hello_world` package
      nsfoxer@nsfoxer-pc ~/temp> tree hello_world
      hello_world
      ├── Cargo.toml
      └── src
          └── main.rs
      
      1 directory, 2 files
      

      cargo会同时构建git项目。使用--vcs none禁用git存储。

    2. 编译:

      cargo build

      将获取所有依赖项。

      nsfoxer@nsfoxer-pc ~/t/hello_world> tree .
      .
      ├── Cargo.lock
      ├── Cargo.toml
      ├── src
      │   └── main.rs
      └── target
          ├── CACHEDIR.TAG
          └── debug
              ├── build
              ├── deps
              │   ├── hello_world-efb3cc30327658a6
              │   └── hello_world-efb3cc30327658a6.d
              ├── examples
              ├── hello_world
              ├── hello_world.d
              └── incremental
                  └── hello_world-2bu9x4t5793bs
                      ├── s-g1qy781nco-1nkogqh-2hbtkajf7iavr
                      │   ├── 1afbzx1y0x1dbpio.o
                      │   ├── 1ko7w99wy4kz2chh.o
                      │   ├── 28cobucdo6rhinaa.o
                      │   ├── 2goevba305ege8v6.o
                      │   ├── 4d0psjpawt67vlja.o
                      │   ├── 5byta3abs29cbrhj.o
                      │   ├── 5dygtqqmhu41ip90.o
                      │   ├── dep-graph.bin
                      │   ├── gfq0hw1bwbsq84d.o
                      │   ├── query-cache.bin
                      │   └── work-products.bin
                      └── s-g1qy781nco-1nkogqh.lock
      
      9 directories, 20 files
      

      Cargo.lock包含依赖相关信息。使用--release开启编译优化。

      Caogo.toml包含项目的各种元信息。

    3. 运行:

      ./target/debug.hello_world

    可以使用cargo run进行编译和运行。

    可以使用cargo check进行代码检查。

    目标

    cargo允许Rust项目声明其各种依赖,并保证始终可重复构建。

    1. 引入两个含有各种信息的元数据文件;
    2. 获取并构建项目的依赖项;
    3. 调用rustc或其他工具进行构建;
    4. 使用 Rust 项目的约定(规范/风格)。

    依赖

    crates.io是 Rust 社区的中央存储库,用作发现和下载包的位置。cargo默认配置为,使用它来查找请求的包。

    添加依赖

    [dependencies]
    time = "0.1.12"
    regex = "0.1.41"
    

    cargo build将会获取依赖。如果regexcrates.io上更新了,在我们选择cargo update之前,我们仍会使用相同的版本进行构建.

    文件布局

    .
    ├── Cargo.lock 			
    ├── Cargo.toml
    ├── benches  	# 基准测试
    │   └── large-input.rs
    ├── examples 	# 示例
    │   └── simple.rs
    ├── src
    │   ├── bin # 其他可执行文件
    │   │   └── another_executable.rs
    │   ├── lib.rs # 库文件
    │   └── main.rs # 可执行文件
    └── tests # 集成测试
        └── some-integration-tests.rs
    

    Carogo.toml && Carogo.lock

    • Cargo.toml是从广义上描述你的依赖,并由你编写.
    • Cargo.lock包含有关您的依赖项的确切信息。它由 Cargo 维护,不应手动编辑.
    • 构建其他项目要依赖的库,请将Cargo.lock放置在你的.gitignore
    • 构建可执行文件,如命令行工具或应用程序,请检查Cargo.lock位于git管理下。
    [dependencies]
    rand = { git = "https://github.com/rust-lang-nursery/rand.git", rev = "9f35b8e" }
    

    如上,依赖于github上的项目,当未指定其他信息时默认依赖github上的最新版本。例子中rev指定github的版本。

    Cargo.lock会记录项目第一次构建时依赖版本,此时不需要进行手动添加加具体版本。

    当我们准备选择,更新库的版本时,Cargo 会自动重新计算依赖关系,并为我们更新内容:

    $ cargo update           # updates all dependencies
    $ cargo update -p rand   # updates just “rand”
    

    测试

    cargo test:会查找srctests的测试

    通用编程

    基本类型

    • 整型

      有符号 无符号 长度(字节)
      i8 u8 1
      i16 u16 2
      i32 u32 4
      i64 u64 8
      isize usize arch

      其中,isize表示与机器架构一致。

      进制 示例
      decimal 98_222
      hex 0xff
      Octal 0o77
      Binary 0b1110_0000
      Byte(u8 only) b'A'

      :此处与c不同,byte属于整形,占位1字节。而char占位4字节。

    • 浮点数

      类型:f32f64

      现代CPU在32位和64位浮点数运算效率几乎一致。Rust默认使用64位浮点数。

    • bool

      值: truefalse,占位1字节。

    • 字符

      char:占位4字节,代表一个Unicode标量。

    复合类型

    • 元组

      长度固定,不能改变长度大小。但内部类可以不一致。

      let tup : {i32, f64, u8} = {500, 6.4, 1}; // 此处类型注解可以推断(?)
      
      // 类似ES6,模式匹配  解构
      // 等价于 x = tup.0
      // 		y = tup.1
      // 		z = tup.2
      let {x, y, z} = tup;
      
    • 数组

      与c不同,rust的数组长度固定。内部类型也需要一致。

      let a:[i32; 5] = {1, 2, 3, 4, 5}
      
      // 创建相同元素的数组
      // 以下写法等价于 let a = [3, 3, 3, 3, 3]
      let a = [3; 5]
      
      // 数组访问
      let first = a[0];
      

      当访问数组越界时,程序会崩溃。rust内部包含了运行检查。

    函数

    rust使用snake case进行命名,所有字母小写,以下划线分隔单词。

    被调用函数定义在调用函数范围内即可。

    fn main() {
        another_function(5, 6);
    }
    fn another_function(x : i32, y : i32) {
        println!("Another Funcion!");
    }
    

    函数参数必须显式声明,编译器由此不需要其他代码对参数类型进行推断。

    rust是基于表达式(表达式是会计算并返回值;语句是只执行操作,但不返回值)

    let x = 5; // 语句
    let x = y = 6; // error
    
    // 调用函数,调用宏,{}都是表达式
    let y = {
        let x = 3;
        x + 1
    } // y == 4
    

    rust的函数返回值,等价于最后一个表达式的返回值,可以隐式返回:

    fn five() -> i32 {
        5
    }
    

    控制流

    • if表达式

      条件表达式必须产生一个bool值。

      let number = 5;
      
      if number < 5 {
          // ....
      } else {
          // .....
      }
      
      if number { // error, rust不会将非bool类型转换为bool类型
          
      }
      

      if是一个表达式,可以直接生成值。但每条分支返回值类型应该相同。

      let condition = true;
      let number = if condition {
          5
      } else if somthing{
          6
      } else {
          "seven" // error, 返回值类型不同 
      };
      
    • loop

      可以在将break后的值,作为返回值

      let mut counter = 0;
      
      let result = loop {
          counter += 1;
          if counter == 10 {
              break counter * 10;
          }
      };
      
    • while

    • for

      使用for进行迭代

      let a = [10;9];
      
      for element in a.iter() {
          println!("{}", elment);
      }
      

    所有权

    基本规则

    • 每个值都有一个对应的变量作为其所有者
    • 在同一时间内,有且只有一个所有者
    • 当所有者离开其作用域时,其持有的值就会被释放

    ​ 变量使用的内存在变量本身离开作用域后自动进行释放,(调用drop()的特殊函数)。为避免二次内存释放,rust在变量被移动时(复制到其他变量),将把此变量废弃,不再使用。rust不会自动深度拷贝,任何自动的赋值操作都可以被认为是高效的。

    深度拷贝使用clone()方法。

    拥有Copy的trait的变量类型赋值不会被废弃:所有整型 bool char 浮点型 内部字段全可Copy的元组

    引用和借用

    当变量赋值时,所有权就转移。当希望调用函数保留参数的所有权,必须将参数返回。

    fn main() {
        let s1 = String::from("ADMIN");
        let (s2, len) = calculate_length(s1); // s2想保留s1的所有权,必须将s1返回,否则函数参数s获得s1的所有权,在函数结束后,s离开作用范围,内存被释放。
    
        println!("The length of '{}' is {}.", s2, len);
    }
    
    fn calculate_length(s:String) -> (String, usize) {
        let length = s.len();
        return (s, length);
    }
    

    引用允许在不获得所有权下,使用值。

    fn main() {
        let s1 = String::from("ADMIN");
        let len = calculate_length(&s1);
    
        println!("The length of '{}' is {}.", s1, len);
    }
    
    fn calculate_length(s:&String) -> usize {
        s.len() // s不会销毁s1的数据,因为s没有所有权
    }
    

    引用默认是不可变的,不能修改值。

    可变引用

    添加mut来接收可变引用。

    fn main() {
        let mut s1 = String::from("ADMIN");
        add_string(&mut s1);
        println!("The length of '{}' is {}.", s1, s1);
    }
    
    fn add_string(s : &mut String) {
        s.push_str(" World!");
    }
    

    可变引用的限制:特定作用域的特定数据,一次只能声明一个可变引用。可以多次普通引用,但不能在有可变引用的情况下,再次普通引用。(类似于读写锁,可以同时有多个读,但是只能有一个写)

    let mut s = "admin";
    
    let r1 = &mut s; 
    let r2 = &mut s; // 错误,多次引用可变
    

    悬垂引用:悬垂指针,指针指向某一内存,但此内存在其他地方被释放。rust使用生命周期防止悬垂引用。

    切片

    字符串切片

    fn main() {
        let s1 = String::from("ADMIN WORLD JKL");
        println!("{}", frist_word(&s1));
    }
    fn frist_word(s: &String) -> &str {
        let bytes = s.as_bytes();
    
        for (i, &item) in bytes.iter().enumerate() {
            if item == b' ' {
                return &s[0..i];
            }
        }
        return &s[..];
    }
    
    let a = [1, 2, 3, 4, 5];
    let slice = &a[1..3];
    

    结构体

    // 添加debug来使用 {:?} debug输出 
    #[derive(Debug)]
    
    struct User {
        username: String,
        email: String,
        active: bool,
        sign_in_count: u64,
    }
    
    // 结构体的方法,可以有多个impl
    impl User {
        fn build_user(email:String, username: String) -> User {
            User {
                email,
                username,
                active: true,
                sign_in_count: 1,
            }
        }
    }
    
    impl User {
        // 方法,第一个参数永远是self,一般为引用类型,直接self一般用于强转类型
        fn add_sign(& mut self, number: u64) {
            self.sign_in_count += number;
        }
    }
    
    fn main() {
        // 定义user1,字符串来自String方法,否则错误。
        let user1 = User::build_user(String::from("ex.@email.org"), String::from("example"));
        let mut user2 = User {
            email: String::from("test@q.org"),
            username: String::from("test2"),
            ..user1  // 此处为简写,类似JavaScript中的ES6语法: 同名直接赋值
        };
        user2.add_sign(32);
        println!("user1: {:?}", user1);
        println!("user2: {:?}", user2);
    
        // 结构元祖
        struct Color(i32, i32, i32);
        struct Point(i32, i32, i32);
    
        // black 和 origin不能相互赋值,因为类型不同
        let mut black = Color(0, 0, 0);
        let mut origin = Point(0, 0, 0);
    }
    

    枚举enum和模式匹配match

    enum

    #[derive(Debug)]
    // 枚举可以和相关数据直接关联
    enum Message {
        Quit,  // 没有关联任何数据  
        Move{x: i32, y: i32},  // 包含匿名结构体
        Write(String), // 包含String
        ChangeColor(i32, i32, i32), // 包含三个i32
    }
    fn main() {
        let msg = Message::ChangeColor(0, 0, 0);
        println!("msg {:?}", msg);
    }
    

    Option

    rust处理空值使用枚举Option处理,Option也会被预导入,定义为:

    enum Option<T> {
    	Some(T),
    	None
    }
    
    fn main() {
        let x: i8 = 8;
        let y: Option<i8>  = Some(9);
        
        // error! x 和 y 类型不同
        let sum = x + y;
    }
    

    match

    #[derive(Debug)]
    enum A {
        Alibaba,
        Tencent,
        Baidu,
    }
    
    enum B {
        US,
        CN,
        Coo(A),  // 将Coo绑定一个A枚举
    }
    fn main() {
        let t = B::Coo(A::Alibaba);
        let value = match t {
            B::US => 1000,
            B::CN => 2000,
            B::Coo(coo) => {
                println!("coo: {:?}", coo);
                20
            },
        };   
        println!("value: {:?}", value);
    //    println!("t: {:?}", t);
    }
    

    模式匹配必须匹配完所有类型,可以使用通配符_代替其他所有值。

    可以使用if let 来简单控制。

    包管理

    模块系统:

    • package
    • 单元包 crate
    • 模块 module
    • 路径 path

    模块定义

    mod front_of _house {
        mod hosting {
            fn add() {}
        }
    }
    

    路径

    模块引用 绝对路径相对路径

    pub fn eat() {
        // 绝对路径
        crate::front_of_house::hosting::add();
        // 相对路径
        front_of_house::hosting::add();
    }
    
    • 使用pub暴露路径

    • super类似..,上一级路径

    • 使用use导入路径

    • as重命名

    • pub use重导出

    use std::fmt::Result;
    use std::io::Result as IOResult;
    use std::*;
    

    通用集合类型

    动态数组

  • 相关阅读:
    SpringBoot配置默认日志logback
    SpringBoot静态资源映射、拦截器、过滤器使用
    windows系统安装两个Mysql服务
    SpringBoot使用Jsp开发Web项目
    SpringBoot使用Thymeleaf开发Web项目
    SpringBoot启动原理
    javascript Date
    UDP发送数据测试
    自动迁移
    EF 自测例子
  • 原文地址:https://www.cnblogs.com/nsfoxer/p/15196551.html
Copyright © 2020-2023  润新知