0

【Rust】初学 笔记

 1 year ago
source link: https://www.guofei.site/2022/08/27/rust1.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
# 编译
rustc main.rs

# 运行
./main

cargo:系统和包管理器

cargo --version

# 创建一个新的cargo项目
cargo new learn_rust_project

# 编译
cargo build

# 编译并运行代码
cargo run

基本语法

// 加上 mut 是可变的(mutable),不加是不可变的
let mut guess = String::new();


io::stdin()
    .read_line(&mut guess)
    .expect("Failed to read line");

//
println!("You guessed: {}", guess);

导入别的项目

rand = "0.8.3"
// Crates.io 是 Rust 生态

猜数字的代码

use std::cmp::Ordering;
use std::io;
use rand::Rng;

fn main() {
    println!("Guess the num!");

    let secret_num = rand::thread_rng().gen_range(1..101);

    println!("secret num is {}", secret_num);


    loop {
        println!("Please input you guess:");

        //???为啥要在loop中定义
        let mut guess = String::new();

        io::stdin()
            .read_line(&mut guess)
            .expect("Failed to read line");


        // let guess: u32 = guess.trim().parse().expect("Please type a number!");
        // 用 match,而不是 expect,匹配抛出的错误,并做相应的处理
        let guess: u32 = match guess.trim().parse() {
            Ok(num) => num,
            Err(_) => {
                println!("pleas input a num!");
                continue;
            }
        };


        println!("You guessed: {}", guess);


        match guess.cmp(&secret_num) {
            Ordering::Less => println!("Too small"),
            Ordering::Greater => println!("Too big!"),
            Ordering::Equal => {
                println!("You win!");
                break;
            }
        }
    }
}

变量

  • 可变变量 let mut x = 1
  • 不可变变量 let x = 1
  • 常量 const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
    • 运行的整个过程中有效

遮蔽

fn main() {
    let x = 5;
// x = 5
    let x = x + 1;
    // x = 6

    {
        let x = x * 2;
        // x = 12
        println!("The value of x in the inner scope is: {}", x);
    }

    println!("The value of x is: {}", x);
    // 内部遮蔽结束,这里 x = 6
}
// 不同类型可以遮蔽 1)必须不能加 mut,2)必须是不同类型
let x = "abcd";
let x = x.len();

数据类型

  • rust是静态类型,必须编译时知道所有变量的类型
长度 有符号类型 无符号类型
8 位 i8 u8
16 位 i16 u16
32 位 i32 u32
64 位 i64 u64
128 位 i128 u128
arch isize usize
  • 浮点型 f32, f64
  • bool类型 bool
  • 元组
    • 赋值: let tup: (i32, f64, u8) = (500, 6.4, 1);
    • 取值1:let (x, y, z) = tup;
    • 取值2: let y = tup.1;
  • 数组
    • 赋值 let a: [i32; 5] = [1, 2, 3, 4, 5];
    • 赋值2: let a = [3; 5]; 这是3重复5次
    • 取值:a[0]

结构体

struct User {
    active: bool,
    username: String,
    email: String,
    sign_in_count: u64,
}

fn main() {
    // 不允许单独一个字段可变,只能整体全都可变
    let mut user1 = User {
        email: String::from("[email protected]"),
        username: String::from("someusername123"),
        active: true,
        sign_in_count: 1,
    };

    // 这样改字段值
    user1.email = String::from("[email protected]");
}
let email = String::from("[email protected]");
let username = Strin::from("name");

let mut user2 = User {
  // 简写:email:email,
    email,
    username,
    active: true,
    sign_in_count: 2,
};

简写2:复制别的值进来

let user3 = User {
    email: String::from("321@"),
    ..user1
};
// user1就不能用了,因为它的 username 已经移动过来了
// 不过 active 和 sign_in_count

元组结构体:没有字段名的结构体

struct Color(i32, i32, i32);

fn main() {
    let black = Color(0, 0, 0);
}

没有字段的结构体

struct AlwaysEqual;

fn main() {
    let subject = AlwaysEqual;
}

结构体的方法

#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    // &self 是 self: &Self 的缩写
    // 这里的 &self 指的是 rectangle: &Rectangle
    // 加 & 是因为不想获取所有权,只希望读,而不希望写
    fn area(&self) -> u32 {
        self.width * self.height;
    }
}

方法和字段可以同名

#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    fn width(&self)->bool{
        self.width>0
    }
}

impl 也可以不接一个方法

impl Rectangle {
    fn square(size: u32) -> Rectangle {
        Rectangle {
            width: size,
            height: size,
        }
    }
}

枚举 enum

#![allow(unused)]

fn main() {
    enum IpAddrKind {
        // 可以是不同类型
        V4(u8, u8, u8, u8),
        V6(String),
    }

    let four = IpAddrKind::V4;

    // IpAddrKind 可以像一个类型一样使用
    let six: IpAddrKind = IpAddrKind::V6(String::from("::1"));
}

match

enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter,
}

fn value_in_cents(coin: Coin) -> u8 {
    match coin {
        Coin::Penny => {
            println!("Lucky penny!");
            1
        }
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter => 25,
    }
}

match 中的 othor

fn main() {
    let dice_roll = 9;
    match dice_roll {
        3 => add_fancy_hat(),
        7 => remove_fancy_hat(),
        other => move_player(other),
    }

    fn add_fancy_hat() {}
    fn remove_fancy_hat() {}
    fn move_player(num_spaces: u8) {}
}

如果 other 没用,可以用下划线代替

fn main() {
    let dice_roll = 9;
    match dice_roll {
        3 => add_fancy_hat(),
        7 => remove_fancy_hat(),
        _ => reroll(),
    }

    fn add_fancy_hat() {}
    fn remove_fancy_hat() {}
    fn reroll() {}
}

到 other 后什么也不做

fn main() {
    let dice_roll = 9;
    match dice_roll {
        3 => add_fancy_hat(),
        7 => remove_fancy_hat(),
        _ => (),
    }

    fn add_fancy_hat() {}
    fn remove_fancy_hat() {}
}

collections-vector

fn main() {
    // 如何创建1
    let mut v1: Vec<i32> = Vec::new();
    // 如何创建2
    let v2 = vec![1, 2, 3];

    // push
    v1.push(5);
    v1.push(6);

    // 删?

    // 改
    v1[1] = 9;
    println!("{:?}", v1);

    // 查
    let a = v1.get(0);
    // Some 类型
    // index 超出的话,为 None 类型
    let a2 = v1.get(100);

    let b = v1[0];
    let c = &v1[0];
    println!("{:?},{:?},{},{}", a, a2, b, c);


    v1.push(30);
    // println!("{:?}", a); // 上面 push 时被借用了,所以这里会报错


    println!("{:?}", v1);

    // 循环遍历
    for i in v2 {
        println!("{}", i);
    }

    // 如何在循环遍历中改它们
    for i in &mut v1 {
        *i += 50;
    }
    println!("{:?}", v1);
}

collections-string

fn main() {
    // 如何创建1
    let mut s1: String = String::new();

    // 如何创建2
    let s2_1: &str = "hello, world!";
    let s2: String = s2_1.to_string();

    // 如何创建3
    let s3: String = String::from("hello");
    // 如何转回去
    let s_str: &str = &*s3;


    // t添加
    s1.push('a');
    let s_append = "abb";
    s1.push_str(s_append);

    println!("{}", s1);

    // 用 format连接
    let s_total: String = format!("{}-{}-{}", s1, s2, s3);
    println!("{}", s_total);
}
  • String实际上是 Vec<u8>,所以 len() 对应的是 Vec 的长度
  • 不要用 &s[0..9] 或者 s[0],遇到中文等会 panic

遍历字符串

fn main() {
    let s = String::from("你好rust语言");

    // 遍历字符
    for i in s.chars() {
        println!("{}", i);
    }

    // 这个是遍历 ascii 值,都在 0~255 之间
    for i in s.bytes() {
        println!("{}", i);
    }
}

collections-hash map

use std::collections::HashMap;

fn main() {
    // 新建
    let mut hash_map = HashMap::new();

    // 新建2(实战中往往要这么用)
    let keys = vec![String::from("blue"), String::from("red")];
    let values = vec![10, 30];
    let hash_map2: HashMap<_, _> = keys.iter().zip(values.iter()).collect();
    println!("{:?}", hash_map2);


    // 增&改
    hash_map.insert(String::from("Blue"), 10);
    hash_map.insert(String::from("Red"), 20);
    // 注:insert 之后,HashMap 将获得所有权
    // 查重复 insert 同一个 key 将覆盖


    // 查1
    let val_opt: Option<&i32> = hash_map.get("Blue");
    // 返回一个 Option
    let _b = match val_opt {
        Some(x) => println!("x={}", x),
        None => println!("None")
    };

    // 查2
    for (key, val) in &hash_map {
        println!("k:v = {}:{}", key, val);
    }

    // 改2:循环中改

    for (key, val) in &mut hash_map {
        println!("k:v = {}:{}", key, val);
        *val += 100;
    }


    // 删
    hash_map.remove("red1");


    println!("{:?}", hash_map);
}

HashSet

let mut hash_set = HashSet::new();

// 插入
hash_set.insert(String::from("key1"));
hash_set.insert(String::from("key2"));

// 删除
hash_set.remove("key3"); // 不存在不会报错

// 清空
hash_set.clear();

控制流程


let y = {
    let x = 3;
    x + 1
};
// y=4,因为代码块最后一句是它的返回值

// 函数的最后一句也是返回值,但最后一句不要加分号
fn my_func(x: i32) -> i32 {
    x + 1
}
// 1. 不会自动转换为 bool 值,必须手动转换
if x < 0 {
    println!("x<0")
} else if x < 1 {
    println!("0<=x<1")
} else { println!("x>=1") }

if 用于赋值

let age = 20;
// 每个分支必须都是相同类型
let sex = if age > 18 { "adult" } else { "child" };

loop:不停的执行

  1. 可以与break,continue连用
    let mut x = 1;
    loop {
     x = x + 1;
     if x > 20 {
         break;
     } else if x % 3 == 0 { continue; }
     println!("{}", x);
    }
    
  2. break 可以像 return 一样使用,然后赋值
    let mut cnt = 1;
    let res = loop {
     cnt += 1;
     if cnt > 10 {
         break "end";
     }
    };
    

while 条件循环

while cnt < 10 {
    cnt += 1;
}

for 循环

let lst = [10, 20, 30, 40, 50];

for element in lst {
    println!("the value is: {}", element);
}


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

所有权

所有权让 Rust 无需 GC

内存管理的方式

  • 垃圾回收机制,不断寻找不再使用的内存
  • 开发者写明内存分配和释放
  • Rust:所有权机制

所有权规则

  • 每一个值都有一个 owner (变量)
  • 值在任一时刻有且只有一个所有者。
  • 当owner(变量)离开作用域,这个值将被丢弃。
{
    let a = 1;
}
// a 的作用域结束,这里失效了
println!("{}", a); //所以报错
let x = 5;
let y = x; // 这里发生了 Copy,所以可以正常运行
println!("{}{}", x, y);

// 但是这样就会报错,因为 s1 的数据被“移动”到s2了
let s1 = String::from("hello");
let s2 = s1;
println!("{}{}", s1, s2);

// 改成这样就可以了
let s1 = String::from("hello");
let s2 = s1.clone();
println!("{}{}", s1, s2);

如果类型实现了 copy trait,那么就可以用等号。哪些实现了 copy trait 呢:

  • 所有的整型、浮点型,例如 u32, f64
  • bool 类型
  • 字符类型 char
  • 元组,当且仅当其包含的类型也都实现 Copy 的时候。比如,(i32, i32) 实现了 Copy,但 (i32, String) 就没有。

所有权与函数

向函数传值的时候,等同于赋值,所以规则是同上的

fn main() {
    let s = String::from("hello");
    takes_ownership(s);
    // println!("{}", s); // 所有权已经没了,会报错
    let x = 5;
    makes_copy(x); // 因为是 copy 的,所以所有权还在
    println!("{}", x); // 不会报错
}

fn takes_ownership(some_string: String) {
    // 运行结束,some_string 调用了 drop 方法,内存释放
}

fn makes_copy(some_integer: i32) {
//     这里的some_integer 也会调用 drop 方法,但它是 copy 来的,对之前的值没有影响
}

另外,函数return一个值的时候,所有权转移出去了(因此不会drop掉)
(代码略)

  • 但不要 return 它的引用,因为对应的值会drop。进而导致这个引用指向一个无效的内存。Rust 编译器不通过

借用

上面这种情况,把一个 String 传入函数后,就失去所有权了。但是x1 往往不希望这种情况发生。所以有了借用的概念

fn main() {
    let s1 = String::from("hello");

    // &s1 创建了一个指向 s1 的引用,但没有其所有权。
    // 函数可以使用 s1,但不获取所有权
    let len = calculate_length(&s1);

    println!("The length of '{}' is {}.", s1, len);
}

fn calculate_length(s: &String) -> usize {
  // 因为没有所有权,所以不能更改。例如,不能 s.push_str("aaaa");
    s.len()
//     因为没有所有权,所以函数结束后不触发 drop
}

但是x2 有时候还想让它在函数中可变,这就需要使用 可变借用

fn main() {
    // 1. s1 要加 mut,因为 s1 是要改动的
    let mut s1 = String::from("hello");

    // 2. &mut 表示可变引用
    let len = calculate_length(&mut s1);

    println!("The length of '{}' is {}.", s1, len);
    // s1 变成了 'helloaaaa'
}

// 3. 声明函数的时候,也要明确加 &mut
fn calculate_length(s: &mut String) -> usize {
    // 就可以改 s 了
    s.push_str("aaaa");
    s.len()
}

但是x3,同一个值,同一时间,只能有一个可变引用。这是为了防止数据竞争(data race),它会导致程序结果不可知。

fn main() {
    let mut s = String::from("hello");

    let r1 = &mut s;
    let r2 = &mut s;

    println!("{}, {}", r1, r2); // 报错
}
// 进一步说,一个可变引用+一个不可变引用,也会报错
// 进一步所x2,不可变引用的最后一次使用后,又声明了可变引用,就没有问题
fn main() {
    let mut s = String::from("hello");

    let r1 = &s; // 没问题
    let r2 = &s; // 没问题
    println!("{} and {}", r1, r2);
    // 此位置之后 r1 和 r2 不再使用

    let r3 = &mut s; // 没问题
    println!("{}", r3);
}

但是x4,如果用大括号限制一下作用域,就不存在同时拥有权限的问题

// 不会报错
let mut s = String::from("hello");
{
    let r1 = &mut s;
    println!("{}", r1);
}

let r2 = &mut s;
println!("{}", r2);

slice

let mut s = String::from("hello world");
let s1_1: &str = &s[3..s.len()];
let s1_2 = &s[3..];
let mut s2_1 = &s[..];
println!("{},{},{}", s1_1, s1_2, s2_1);

智能指针

简单的是用 Box 实现的,Box 允许你把值放到堆上,而不是栈上。

fn main() {
    let b = Box::new(5);
    println!("b = {}", b)
}

特性参考代码

下面用 & 防止被借用,这样main函数可以继续使用 rect

struct Rectangle {
    width: u32,
    height: u32,
}

fn main() {
    let rect = Rectangle {
        width: 30,
        height: 50,
    };

    println!(
        "The area of the rectangle is {} square pixels.",
        area(&rect)
    );
}

fn area(rect: &Rectangle) -> u32 {
    rect.width * rect.height
}

打印结构体

// 1. 需要加上下面这行
#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

fn main() {
    let rect = Rectangle {
        width: 30,
        height: 50,
    };
    println!("Rect is {:?}", rect);
    // 下面这个格式更好看
    println!("Rect is {:#?}", rect);
}
Rect is Rectangle { width: 30, height: 50 }
Rect is Rectangle {
    width: 30,
    height: 50,
}

参考

https://rustwiki.org/zh-CN/book/ch03-05-control-flow.html


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK