文档章节

Rust语言开发基础(七)Rust 特性

austenliao
 austenliao
发布于 2016/03/25 10:09
字数 2673
阅读 878
收藏 6
点赞 3
评论 0

这部分是Rust语言的核心部分,掌握起来有一定难度,特别是生命周期部分,让人有Rust的学习曲线陡升的感觉,爬过这座高峰,其它皆坦途。
这部分也是让人觉得Rust语言比其它语言如C/C++等复杂的主要原因之一,即使是写文章介绍起来也感觉不容易。

一、所有权(ownership)
基本概念:一个变量同一个时刻只能有一个拥有者。
所有权概念使Rust确保了对于任何给定的资源都正好(只)有一个绑定与之对应。
如同一个房子只能有一个主人,let的操作过程如同政府给这个主人颁发了房产证。

1. 所有权是变量绑定的一个属性,发生来let的过程中。

let v = vec![1, 2, 3]; 
let v2 = v;

第一行为向量(vector)对象和它包含的数据分配了内存。向量对象储存在栈上并包含一个指向堆上  [1,2, 3] 内容的指针。
第二行当我们从  v 移动到  v2 ,它为  v2 创建了一个那个指针的拷贝。这意味着这将会有两个 所有权指向向量内容的指针。这将会因为引入了一个数据竞争而违反Rust的安全保证。因此,Rust禁止我们在移 动后使用  v 。
总结:了当所有权被转移给另一个绑定以后,你不能再使用原始绑定。

2.一些特殊情况不会出现这种所有权限制:
情况1:trait,它会实现copy。(trait类似接口定义的关键字,如java的interface)
情况2:所有基本类型会自动实现copy。
let v = 1;
let v2 = v;
println!("v is: {}", v);

在这个情况,  v 是一个  i32 ,它实现了  Copy 。这意味着,就像一个移动,当我们把  v 赋值给  v2  ,产生了 一个数据的拷贝。不过,不像一个移动,我们仍可以在之后使用  v 。这是因为  i32 并没有指向其它数据的 指针,对它的拷贝是一个完整的拷贝。

二、借用和引用(borowwing and Reference)

引用类似于C语言的指针,连符号都是一样的:&, &point,&a
首先定义 let a = vec![1, 2, 3] 假设首先定义一座可以住3个人的大房子,房主是a。
&a 意味着引用a的资源,就像出租屋管理中心登记了房子a的门牌号,以后找这座房子a就方便了
那么let b = &a, 意味a将房子出租给b,a虽然仍是房子的主人,但是由于将房子出租给了b,在出租期间,房主人a是不能使用的。
正式的说法就是,b借用了a的资源,至于借出的期限是多少,这个涉及下个节的生命周期,在这个期限内,a不能使用,只有过了这个期限,才可以重新使用。
如果是let b = &mut a, 这个是最大权限的借出了,借出期间,b可以随便修改房子里的东西。


第一种类型的引用:&T,我们称  &T 类型为一个”引用“,而与其拥有这个资源,它借用了所有权。一个借用变量的 绑定在它离开作用域时并不释放资源。这意味着  foo() 调用之后,我们可以再次使用原始的绑定。
引用是不可变的,就像绑定一样。这意味着在中,向量完全不能被改变:
fn foo(v: &Vec<i32>) {
     v.push(5);
}
let v = vec![];
foo(&v);
上述代码是会报错的

第二种类型的引用:&mut T,称为“可变引用”,即允许你改变你借用的资源。
首先,被引用变量必须也是mut类型。
let mut x = 5;
let y = &mut x;  //x资源给y借用了
*y += 1;                                         
println!("{}", x);// 这里尝试借用x
                  // &mut x 的借用到这里才结束
=============================以上错误,这些作用域冲突了:我们不能在  y 在作用域中时生成一个  &x 。
let mut x = 5;
{
    let y = &mut x;
    *y += 1;
}
println!("{}", x);
=============================以上正确,用{}限定了范围
这会打印  6 。我们让  y 是一个  x 的可变引用,接着把  y 指向的值加一。你会注意到  x 也必须被标记为  mut ,如果它不是,我们不能获取一个不可变值的可变引用。
我们的可变借用在我们创建一个不可变引用之前离开了作用域。

借用(borowwing)有一些规则
第一,任何借用必须位于比拥有者更小的作用域。(真正主人的使用期限当然要大于借用者的使用期限)
第二,你可以有一个或另一个这两种类型的借用,不过不能同时拥有它们。(东西不能同时借给很多人用,轮流用才行)
0个或N个资源的引用(&T)。
只有1个可变引用((&mut T)。

通过引用,你可以拥有你像拥有的任意多的引用,因为它们没有一个在写。如果你在写,并且你需要2个或 更多相同内存的指针,则你只能一次拥有一个  &mut 。这就是Rust如何在编译时避免数据竞争:我们会得到 错误,如果我们打破规则的话。

三、生命周期(困难曲线陡升的东西)
1. http://www.rust.cc/t/rustde-lifetimede-zuo-yong/712/6
2. https://wiki.rust-china.org/Rust-Lifetime
3. http://stackoverflow.com/questions/17490716/lifetimes-in-rust
4. https://wiki.rust-china.org/Rust-Lifetime
5. 案例:https://github.com/hyperium/hyper/blob/master/src/server/request.rs

1、生命周期是什么?
https://github.com/rustcc/RustPrimer/blob/master/13-ownership-system/13-03-lifetimes.md
理论上讲,生命周期是一种构想,在这个构想里面,编译器将用于确保所有变量的借用是有效的。一个变量的生命周期开始于这个变量被创造的时候,结束于这个变量被摧毁的时候。另外,生命周期lifetime和scope经常给一起提及,但是它们是不一样的。一般来讲,可以通过Scope作用域来理解生命周期,“生命周期”在编译阶段就已经决定变量应该在什么时候销毁内存该什么时候释放,生命周期就是告诉编译器,内存什么时候销毁。

通俗的讲,生命周期就是a借东西给b,借用期限就是这个生命周期了,每个借用都必须有借用期,不能随便借出不还了,想永久霸占是不行滴,当然有时候在一定条件下,这东西如果借给你复制拷贝一下,也是可以的。lifetime和scope可以理解成使用期限和使用范围。

总之,要理解生命周期,必须先要完全理解上面的所有权,引用和借用的概念。

2、生命周期用什么表达?
使用单引号加单个字母:'a

有一个生命周期:foo<'a> 表示foo有个生命周期'a
说明:使用生命周期,需要用到泛型<>,这种表达意味着foo的生命周期不可能超过它的泛型'a
类型的精确的注解还有 & 'a T 这种形式,
有多个生命周期:foo<'a, 'b>
说明:这种表达以为foo的生命周期不可能超过它的泛型'a 或'b


3、什么场合要用到生命周期
函数
- any reference must have an annotated lifetime.
- any reference being returned must have the same lifetime as an input or be static.
A.对函数来说,任何引用都必须有一个被注解的生命周期。
B.任何引用被函数返回时,都必须有同样的生命周期被输入或者是静态的参数。
只有函数返回引用时,必须加lifetime,如果不加,编译器不知道返回值的生存期。

实例1,fun1因只有一个入参,返回的lifetime默认跟入参一致,但fun2就必须要加lifetime了
fn main() {
   let mut i = 10;
   let k = fun1(&mut i);
   println!("{:?}", k);

   let mut x = 10;
   let mut y = 5;
   let z = fun2(&mut x, &y);
   println!("{:?}", z);
}

fn fun1(c: &mut i32) -> &i32 {
   *c = *c + 1; c
}

fn fun2<'a,'b>(c1: &'a mut i32, c2: &'b i32) -> &'a i32 {
   *c1 = *c1 + *c2;
   c1
}

每一个借用都会有一个生存期。生存期用来表示借用的有效范围。
Rust对于每一个引用的生命周期都是严格控制的,在编译期就保证了引用的阳寿不会长过它引用的对象,从而确保了“野指针”的 不可发生性 。

 

结构体
struct Foo<'a> {
    x: &'a i32,
}
fn main() {
    let y = &5; // this is the same as `let _y = 5; let y = &_y;`
    let f = Foo { x: y };
    println!("{}", f.x);
}
使用它,是因为我们需要确保任何  Foo 的引用不能比它包含 的  i32 的引用活的更久。
在整个impl块中:
struct Foo<'a> {
    x: &'a i32,
}
impl<'a> Foo<'a> {
    fn x(&self) -> &'a i32 { self.x }
}
fn main() {
    let y = &5; // this is the same as `let _y = 5; let y = &_y;`
    let f = Foo { x: y };
    println!("x is: {}", f.x());
}
如你所见,我们需要在  impl 行为  Foo 声明一个生命周期。我们重复了  'a 两次,就像在函数 中:
impl<'a> 定义了一个生命周期  'a ,而  Foo<'a> 使用它。

4、Lifetime要解决什么问题?

其中一个就是要解决悬垂指针“Dangling pointer”的问题,比如
fn dangling<'a>() -> &'a i32 {
    letlocal_var: i32 = 1;
    &local_var
}


5、关于Lifetime到底是怎么推导的?
生存期是用来做类型限定的,并不是很多人口中说的用来做推断的。

它就是类型系统的一部分,不是有什么用,是必须用,否则就必须放弃今天的以item为范围进行推导而改成在全局推导。

不妨看一下这段程序:
fn echo<'a>(a: &'a str) -> &'a str {
   a
}

如上这个函数echo,它接受一个参数a,然后直接把它返回回去,返回值的Lifetime与参数一致。
那么如果有如下调用:
let ret = echo("hello world");
那么我们就开始推导,首先"hello world"的类型是&'static str,代入到echo中,'a = 'static,因此返回值ret的类型就是&'static str。验证结论的方法是
let ret: &'static str = echo("hello world");
并不会报错。

那么如果我们传别的呢?

fn outer() {
    // ... 其它代码
    let string: String = "hello world".to_owned();
    let ret = echo(&string[..]);
    // ... 其它代码
}

在这种情况下'a应该是什么呢?答案就是string的作用域(Scope),即outer函数体。

© 著作权归作者所有

共有 人打赏支持
austenliao
粉丝 21
博文 17
码字总数 17804
作品 0
广州
技术主管
Rust 0.6 测试版发布,Mozilla 的编程语言

Rust 0.6 发布了,下载地址: http://static.rust-lang.org/dist/rust-0.6.tar.gz http://static.rust-lang.org/dist/rust-0.6-install.exe 该版本已经包含了计划中 Rust 所有的特性,请大家......

oschina
2013/04/01
1K
4
小白必备!Rust 编程语言入门教程

开发者小伙伴们, Rust 您一定要了解一下 最近的一项 Stack Overflow 调查发现, 近 80% 的受访者 都喜欢或希望使用 Rust 语言进行开发。 这个数字真是令人难以置信! 那么 Rust 有什么益处呢...

IBMdW
04/18
0
0
Rust 0.8 发布,Mozilla 的新编程语言

Rust 0.8 发布了,这还是一个 Alpha 版本,详细的改进记录请看这里。 下载地址: http://static.rust-lang.org/dist/rust-0.8.tar.gz http://static.rust-lang.org/dist/rust-0.8-install.e...

oschina
2013/09/27
3.1K
19
使用 Rust 构建分布式 Key-Value Store

欢迎大家前往腾讯云社区,获取更多腾讯海量技术实践干货哦~ 引子 构建一个分布式 Key-Value Store 并不是一件容易的事情,我们需要考虑很多的问题,首先就是我们的系统到底需要提供什么样的功...

腾讯云社区
2017/11/20
0
0
DevOps时代,Rust将承担更伟大的责任!

  【IT168 评论】如今,越来越多的Ruby on Rails开发人员都在转向Rust,Rust极有可能成为下一代基础架构语言。为什么Rust可以如此迅速普及?在时下最流行的Dev/Ops中,Rust又发挥着什么作用...

it168网站
2017/12/11
0
0
Rust 0.2 发布,Mozilla 的编程语言

Mozilla 开发一个新的编程语言,名为“Rust”,由web语言的领军人物Brendan Eich(js之父),Dave Herman以及Mozilla公司的Graydon Hoare 合力开发。 创建这个新语言的目的是为了解决一个很顽...

红薯
2012/03/30
981
7
Rust 1.0.0 蓄势待发

从源码看出,Rust 1.0.0 离最终用户不远了。 Rust 1.0.0 发布计划请看这里:http://www.oschina.net/news/57888/rust-1-0-timeline。 Rust 1.0.0 Alpha 计划是 2015 年 1 月 9 日发布,当 Ru...

曾赛
2015/01/07
4.2K
46
Rust 0.3 发布,Mozilla 的编程语言

Rust 0.3 发布了,Rust 是 Mozilla 目前正在开发一个新的编程语言,由web语言的领军人物Brendan Eich(js之父),Dave Herman以及Mozilla公司的Graydon Hoare 合力开发。 创建这个新语言的目...

oschina
2012/07/13
1K
3
Rust 1.0 正式版发布,Mozilla 编程语言

Rust 1.0 正式版发布了,这是官方首次宣布的 Rust 稳定版本。当然 1.0 版本的发布并不代表 Rust 语言已经完工,还有很多特性需要完成。1.0 版本的发布要特别感谢以下贡献者: Rust 是 Mozill...

oschina
2015/05/16
13.3K
88
Rust 1.0 alpha 发布,Mozilla 新编程语言

Rust 1.0 alpha 发布了,该版本已经完成了所有 1.0 版本的语言特性和核心库。接下来主要是文档完善以及功能改进和 bug 的修复。详细的介绍请看发行说明。 下载地址:http://www.rust-lang.or...

oschina
2015/01/10
2.1K
12

没有更多内容

加载失败,请刷新页面

加载更多

下一页

实现异步有哪些方法

有哪些方法可以实现异步呢? 方式一:java 线程池 示例: @Test public final void test_ThreadPool() throws InterruptedException { ScheduledThreadPoolExecutor scheduledThre......

黄威
今天
0
0
linux服务器修改mtu值优化cpu

一、jumbo frames 相关 1、什么是jumbo frames Jumbo frames 是指比标准Ethernet Frames长的frame,即比1518/1522 bit大的frames,Jumbo frame的大小是每个设备厂商规定的,不属于IEEE标准;...

六库科技
今天
0
0
牛客网刷题

1. 二维数组中的查找(难度:易) 题目描述 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入...

大不了敲一辈子代码
今天
0
0
linux系统的任务计划、服务管理

linux任务计划cron 在linux下,有时候要在我们不在的时候执行一项命令,或启动一个脚本,可以使用任务计划cron功能。 任务计划要用crontab命令完成 选项: -u 指定某个用户,不加-u表示当前用...

黄昏残影
昨天
0
0
设计模式:单例模式

单例模式的定义是确保某个类在任何情况下都只有一个实例,并且需要提供一个全局的访问点供调用者访问该实例的一种模式。 实现以上模式基于以下必须遵守的两点: 1.构造方法私有化 2.提供一个...

人觉非常君
昨天
0
0
《Linux Perf Master》Edition 0.4 发布

在线阅读:https://riboseyim.gitbook.io/perf 在线阅读:https://www.gitbook.com/book/riboseyim/linux-perf-master/details 百度网盘【pdf、mobi、ePub】:https://pan.baidu.com/s/1C20T......

RiboseYim
昨天
1
0
conda 换源

https://mirrors.tuna.tsinghua.edu.cn/help/anaconda/ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/conda config --add channels https://mir......

阿豪boy
昨天
1
0
Confluence 6 安装补丁类文件

Atlassian 支持或者 Atlassian 缺陷修复小组可能针对有一些关键问题会提供补丁来解决这些问题,但是这些问题还没有放到下一个更新版本中。这些问题将会使用 Class 类文件同时在官方 Jira bug...

honeymose
昨天
0
0
非常实用的IDEA插件之总结

1、Alibaba Java Coding Guidelines 经过247天的持续研发,阿里巴巴于10月14日在杭州云栖大会上,正式发布众所期待的《阿里巴巴Java开发规约》扫描插件!该插件由阿里巴巴P3C项目组研发。P3C...

Gibbons
昨天
1
0
Tomcat介绍,安装jdk,安装tomcat,配置Tomcat监听80端口

Tomcat介绍 Tomcat是Apache软件基金会(Apache Software Foundation)的Jakarta项目中的一个核心项目,由Apache、Sun和其他一些公司及个人共同开发而成。 java程序写的网站用tomcat+jdk来运行...

TaoXu
昨天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部