所有权:

  变量具有唯一所有权。如果一个类型拥有 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: ,
}
}

结构体更新语法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(, , );
let origin = Point(, , );

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

类单元结构体unit-like structs

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

结构体数据的所有权

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

struct 类型友好打印:

#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
} fn main() {
let rect1 = Rectangle { width: , height: }; println!("rect1 is {:#?}", rect1);
}

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

#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
//方法
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
} fn main() {
let rect1 = Rectangle { width: , height: }; 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 { width: 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(, , , ); 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();
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>,而非此类型的变量,永远非空,不需要做空值检查。)

Rust 基础学习的更多相关文章

  1. salesforce 零基础学习(五十二)Trigger使用篇(二)

    第十七篇的Trigger用法为通过Handler方式实现Trigger的封装,此种好处是一个Handler对应一个sObject,使本该在Trigger中写的代码分到Handler中,代码更加清晰. ...

  2. 如何从零基础学习VR

    转载请声明转载地址:http://www.cnblogs.com/Rodolfo/,违者必究. 近期很多搞技术的朋友问我,如何步入VR的圈子?如何从零基础系统性的学习VR技术? 本人将于2017年1月 ...

  3. IOS基础学习-2: UIButton

    IOS基础学习-2: UIButton   UIButton是一个标准的UIControl控件,UIKit提供了一组控件:UISwitch开关.UIButton按钮.UISegmentedContro ...

  4. HTML5零基础学习Web前端需要知道哪些?

    HTML零基础学习Web前端网页制作,首先是要掌握一些常用标签的使用和他们的各个属性,常用的标签我总结了一下有以下这些: html:页面的根元素. head:页面的头部标签,是所有头部元素的容器. b ...

  5. python入门到精通[三]:基础学习(2)

    摘要:Python基础学习:列表.元组.字典.函数.序列化.正则.模块. 上一节学习了字符串.流程控制.文件及目录操作,这节介绍下列表.元组.字典.函数.序列化.正则.模块. 1.列表 python中 ...

  6. python入门到精通[二]:基础学习(1)

    摘要:Python基础学习: 注释.字符串操作.用户交互.流程控制.导入模块.文件操作.目录操作. 上一节讲了分别在windows下和linux下的环境配置,这节以linux为例学习基本语法.代码部分 ...

  7. CSS零基础学习笔记.

    酸菜记 之 CSS的零基础. 这篇是我自己从零基础学习CSS的笔记加理解总结归纳的,如有不对的地方,请留言指教, 学前了解: CSS中字母是不分大小写的; CSS文件可以使用在各种程序文件中(如:PH ...

  8. Yaf零基础学习总结5-Yaf类的自动加载

    Yaf零基础学习总结5-Yaf类的自动加载 框架的一个重要功能就是类的自动加载了,在第一个demo的时候我们就约定自己的项目的目录结构,框架就基于这个目录结构来自动加载需要的类文件. Yaf在自启动的 ...

  9. Yaf零基础学习总结4-Yaf的配置文件

    在上一节的hello yaf当中我们已经接触过了yaf的配置文件了, Yaf和用户共用一个配置空间, 也就是在Yaf_Application初始化时刻给出的配置文件中的配置. 作为区别, Yaf的配置 ...

随机推荐

  1. OpenStack 虚拟机启动流程 UML 分析(内含 UML 源码)

    目录 文章目录 目录 前言 API 请求 Nova API 阶段 Nova Conductor 阶段 Nova Scheduler 阶段 Nova Compute 阶段(计算节点资源分配部分) Nov ...

  2. JAVA多线程程序ProgressBar2

    JAVA多线程程序ProgressBar2 题目简介: 思路分析:与上一篇:JAVA多线程程序ProgressBar类似,本篇避免过于冗杂,所以在此没有给出. 实验代码: import java.aw ...

  3. Using Android monkeyrunner from Eclipse, both in Windows and Linux!

    This time I want to use English to make this article useful for all others in the world:) As you kno ...

  4. [笔记] 使用otunnel从外网访问内网

    需求 内网机器没有公网IP,但是可以访问外网,现在需要从外网访问内网机器. 举例,在家里机器A访问公司内网机器B. 前提 需要一台有公网IP的服务器S做中转,这样就可以打通AB两端了. A <- ...

  5. JAVA学习记录<一>

    一: JAVA初体验: 1.JAVA简介: 2.环境搭建: 3:MyEclipse的使用简介: 4:程序的移植:项目的导入,导出. 5:学习JAVA的经验: 多写,多问,总结和复习!!!

  6. 【D3D12学习手记】CPU/GPU Synchronization

    由于有两个并行运行的处理器(CPU和GPU),会出现许多同步问题.假设我们有一些资源R存储了我们希望绘制的某些几何体的位置. 此外,假设CPU更新R的数据以存储位置p1,然后将引用R的绘图命令C添加到 ...

  7. 从内存上看python的对象

    python中有一个说法:一切皆是对象,怎么理解这句话呢?我们可以通过查看数字,字符串在内存中的表示形式来对这句话有个更深的认识. 那么,怎么查看对象在内存中是什么样的呢?可以先参考一些这篇文章:ht ...

  8. AKKA文档2.3(java版)—什么是角色

    原文:http://doc.akka.io/docs/akka/2.3.5/general/actors.html译者:Vitas 什么是角色? 前面角色系统一节介绍了一群角色如何形成一个层次结构,并 ...

  9. 在phpStrom中安装php代码格式化插件Php-cs-fixer

    由于phpStrom原来的插件不再开源,现在转为使用Php-cs-fixer格式化代码.以下为在phpStrom中安装Php-cs-fixer的具体步骤. 安装安装很简单,下载php-cs-fixer ...

  10. 第四周课程总结&试验报告2

    试验报告2 写一个名为Rectangle的类表示矩形.其属性包括宽width.高height和颜色color,width和height都是double型的,而color则是String类型的.要求该类 ...