Rust 基础学习

所有权:

  变量具有唯一所有权。如果一个类型拥有 Copy trait,一个旧的变量在将其赋值给其他变量后仍然可用。除此之外,赋值意味着转移所有权。Rust 不允许自身或其任何部分实现了 Drop trait 的类型使用 Copy trait。

如下是一些 Copy 的类型:

  • 所有整数类型,比如 u32
  • 布尔类型,bool,它的值是 true 和 false
  • 所有浮点数类型,比如 f64
  • 字符类型,char
  • 元组,当且仅当其包含的类型也都是 Copy 的时候。比如,(i32, i32) 是 Copy 的,但 (i32, String) 就不是。

引用:

  引用指的是获取对象或变量的内容而不获取所有权。也意味着不能修改被引用的值。可以存在多个引用。

let s1 = Stri与使用 & 引用相反的操作是 解引用dereferencing),它使用解引用运算符,*ng::from("hello");

let len = calculate_length(&s1);   //引用

与使用 & 引用相反的操作是 解引用dereferencing),它使用解引用运算符 

可变引用:

  为了对引用的对象修改,可以在特定作用域中对特定数据仅存在唯一可变引用

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

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

但,需要注意的是,不能在拥有不可变引用的同时拥有可变引用。即,要么引用,要么可变引用。

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

let r1 = &s; // no problem
let r2 = &s; // no problem
let r3 = &mut s; //  Error, BIG PROBLEM

Slice:

  数组是同一类型的对象的集合 T, 存储在连续内存中。 用方括号 [] 创建数组, 以及它们的大小在编译的时候判定,是它们的类型签名的一部分 [T; size]

切片和数组相似,但它们的大小在编译时是不知道的. 相反,切片是一个双字对象,第一个字是一个指针中的数据,第二个字是切片的长度。切片可借用数组的截面,并具有式签名 &[T]

结构体(struct):

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

字段初始化简写语法field init shorthand):

fn build_user(email: String, username: String) -> User {
    User {
        email,
        username,
        active: true,
        sign_in_count: 1,
    }
}

 结构体更新语法struct update syntax)实现从其他结构体创建实例:

let user2 = User {
    email: String::from("another@example.com"),
    username: String::from("anotherusername567"),
    ..user1
};

使用结构体更新语法为一个 User 实例设置新的 email 和 username 值,不过其余值来自 user1 变量中实例的字段

元组结构体tuple structs

元组结构体有着结构体名称提供的含义,但没有具体的字段名,只有字段的类型。当你想给整个元组取一个名字,并使元组成为与其他元组不同的类型时,元组结构体是很有用的,这时像常规结构体那样为每个字段命名就显得多余和形式化了。

struct Color(i32, i32, i32);
struct Point(i32, i32, i32);

let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);

因为它们是不同的元组结构体的实例。你定义的每一个结构体有其自己的类型,即使结构体中的字段有着相同的类型。例如,一个获取 Color 类型参数的函数不能接受 Point 作为参数,即便这两个类型都由三个 i32 值组成。在其他方面,元组结构体实例类似于元组:可以将其解构为单独的部分,也可以使用 . 后跟索引来访问单独的值,

类单元结构体unit-like structs

没有任何字段,类似于 (),即 unit 类型。类单元结构体常常在你想要在某个类型上实现 trait 但不需要在类型中存储数据的时候发挥作用。

结构体数据的所有权

可以使结构体存储被其他对象拥有的数据的引用,不过这么做的话需要用上 生命周期lifetimes)。

struct 类型友好打印:

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

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

    println!("rect1 is {:#?}", rect1);
}

方法 与函数类似:它们使用 fn 关键字和名称声明,可以拥有参数和返回值,同时包含在某处调用该方法时会执行的代码。不过方法与函数是不同的,因为它们在结构体的上下文中被定义(或者是枚举或 trait 对象的上下文),并且它们第一个参数总是 self,它代表调用该方法的结构体实例。

#[derive(Debug)]
struct Rectangle {
     u32,
    height: u32,
}
//方法
impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }
}

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

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

在 area 的签名中,使用 &self 来替代 rectangle: &Rectangle,因为该方法位于 impl Rectangle上下文中所以 Rust 知道 self 的类型是 Rectangle。注意仍然需要在 self 前面加上 &,就像 &Rectangle 一样。方法可以选择获取 self 的所有权,或者像我们这里一样不可变地借用 self,或者可变地借用 self,就跟其他参数一样。

这里选择 &self 的理由跟在函数版本中使用 &Rectangle 是相同的:我们并不想获取所有权,只希望能够读取结构体中的数据,而不是写入。如果想要在方法中改变调用方法的实例,需要将第一个参数改为 &mut self通过仅仅使用 self 作为第一个参数来使方法获取实例的所有权是很少见的;这种技术通常用在当方法将 self 转换成别的实例的时候,这时我们想要防止调用者在转换之后使用原始的实例

关联函数

impl 块的另一个有用的功能是:允许在 impl 块中定义  以 self 作为参数的函数。这被称为 关联函数associated functions),因为它们与结构体相关联。它们仍是函数而不是方法,因为它们并不作用于一个结构体的实例。你已经使用过 String::from 关联函数了。

关联函数经常被用作返回一个结构体新实例的构造函数。例如我们可以提供一个关联函数,它接受一个维度参数并且同时作为宽和高,这样可以更轻松的创建一个正方形 Rectangle 而不必指定两次同样的值:

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

使用结构体名和 :: 语法来调用这个关联函数:比如 let sq = Rectangle::square(3);。这个方法位于结构体的命名空间中::: 语法用于关联函数和模块创建的命名空间。

每个结构体都允许拥有多个 impl 块。

枚举:

举例:

enum IpAddrKind {
    V4,
    V6,
}

可以像这样创建 IpAddrKind 两个不同成员的实例:

let four = IpAddrKind::V4;
let six = IpAddrKind::V6;

Reference :enum

下边的IpAddr 枚举的新定义表明了 V4 和 V6 成员都关联了 String 值:

enum IpAddr {
    V4(String),
    V6(String),
}

let home = IpAddr::V4(String::from("127.0.0.1"));

let loopback = IpAddr::V6(String::from("::1"));

我们直接将数据附加到枚举的每个成员上,这样就不需要一个额外的结构体了。

用枚举替代结构体还有另一个优势:每个成员可以处理不同类型和数量的数据。IPv4 版本的 IP 地址总是含有四个值在 0 和 255 之间的数字部分。如果我们想要将 V4 地址存储为四个 u8 值而 V6 地址仍然表现为一个 String,这就不能使用结构体了。枚举则可以轻易处理的这个情况:

enum IpAddr {
    V4(u8, u8, u8, u8),
    V6(String),
}

let home = IpAddr::V4(127, 0, 0, 1);

let loopback = IpAddr::V6(String::from("::1"));

另一个示例:

enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
}

这个枚举有四个含有不同类型的成员:

  • Quit 没有关联任何数据。
  • Move 包含一个匿名结构体。
  • Write 包含单独一个 String
  • ChangeColor 包含三个 i32

结构体和枚举还有另一个相似点:就像可以使用 impl 来为结构体定义方法那样,也可以在枚举上定义方法。这是一个定义于我们 Message 枚举上的叫做 call 的方法:

impl Message {
    fn call(&self) {
        // 在这里定义方法体
    }
}

let m = Message::Write(String::from("hello"));
m.call();

方法体使用了 self 来获取调用方法的值。这个例子中,创建了一个值为 Message::Write(String::from("hello")) 的变量 m,而且这就是当 m.call() 运行时 call 方法中的 self 的值。

Option 

  Option是标准库定义的另一个枚举。Rust 并没有空值,不过它确实拥有一个可以编码存在或不存在概念的枚举。这个枚举是 Option<T>,而且它定义于标准库中,如下:

enum Option<T> {
    Some(T),
    None,
}

Option<T> 枚举是如此有用以至于它甚至被包含在了 prelude 之中,你不需要将其显式引入作用域。另外,它的成员也是如此,可以不需要 Option:: 前缀来直接使用 Some 和 None。即便如此 Option<T>也仍是常规的枚举,Some(T) 和 None 仍是 Option<T> 的成员。

<T> 语法是一个泛型类型参数。<T> 意味着 Option 枚举的 Some 成员可以包含任意类型的数据。这里是一些包含数字类型和字符串类型 Option 值的例子:

let some_number = Some(5);
let some_string = Some("a string");

let absent_number: Option<i32> = None;

如果使用 None 而不是 Some,需要告诉 Rust Option<T> 是什么类型的,因为编译器只通过 None 值无法推断出 Some 成员保存的值的类型。

当有一个 Some 值时,我们就知道存在一个值,而这个值保存在 Some 中。当有个 None 值时,在某种意义上,它跟空值具有相同的意义:并没有一个有效的值。

>Option<T>  比空值要好的原因是因为 Option<T> 和 T(这里 T 可以是任何类型)是不同的类型,编译器不允许像一个肯定有效的值那样使用 Option<T>。(也就是不能混用,要使用空类型,必须使用Option<T>,而非此类型的变量,永远非空,不需要做空值检查。)

原文地址:https://www.cnblogs.com/gardenofhu/p/9836392.html