Rust中文翻译12

Page 63

我们已经学习了如何写一些Rust代码了.但是能写Rust代码和能写好的Rust代码又很大区别.

这一章讲述一些相对独立的指南,告诉你如何把的Rust代码更进一步.一些常用的模式和标准库会被提到.你可以随意安排阅读本章的顺序.

4.1 栈和堆

作为一个系统级别的语言,Rust可以在底层进行操作.如果你来自于高层语言,有一些系统编程概念你可能不太熟悉.最重要的就是内存中的栈和堆是如何工作的.如果你了解类C语言中栈的分配,这一章就是一个回顾.如果你不了解,你可以在本章学习这些概念了,但是我们关注在rust语言上.

4.1.1 内存管理

这里有两个概念是关于内存管理的.栈和堆是抽象的概念,可以帮助你决定何时分配内存或释放内存.

这里有一个高层次的对比:

栈是Rust默认的分配内存的地方,它的速度很快.但是这是在函数体中分配的内存,并且大小有限.另一方面,堆的速度慢一点,需要你在程序显示分配.但是到校是无限制的而且是全局可访问的.

Page 64

我们来讨论一下这个Rust程序:

fn main() {

let x = 42;

}

这个程序有一个变量绑定,x.需要在某处分配内存来存放它.Rust默认使用栈来分配内存,也就是说基本类型分配在栈上.这是什么意思呢?

当一个函数被调用后,一些内存被开辟出来用以保存这个函数的所有局部变量和其他一些东西.这称为"栈帧",在本书中我们忽略其他一些额外的信息,只考虑局部变量.在此例中,当main()函数运行时,我们分配了一个32位整型值在我们的栈帧中.这是自动实现的,我们并没有写什么Rust代码来实现这一功能.

当函数结束的时候,它的栈帧将要被释放.这也是自动完成的,我们没有做什么特殊的工作.

这个简单的例子就这样结束了.关键点是你需要理解栈的分配是相当相当快的.如果我们可以提前知道我们所有的局部变量,我们就可以对内存来一次总揽.因为我们可以一次就把所有的局部变量释放了,我们也就可以很快的释放这些内存.

缺点是我们不能在函数结束以后再保持这些局部变量了.我们还没有讨论栈这个字的含义呢.为此,我们需要一个稍微复杂一点的例子:

fn foo() {

let y = 5;

let z = 100;

}

fn main() {

let x = 42;

foo();

}

这个程序中含有3个局部变量:2个在foo(),1个在main().像之前的例子一样,当main()函数调用的时候,一个整型被分配在了栈上.但是当我们展示foo()调用的时候发生了什么,我们需要先把内存画出来.在你的操作系统看来内存是一个很简单的东西:一个很大的地址链表,从0开始到一个很大的数,就是你的内存能够容纳的大小.例如,如果你的内存有1G大小,你的地址从0到1,073,741,824.这个数来自于230,也就是1GB内存所包含的字节数.

Page 65

这个内存就像一个巨大的数组:地址从0开始然后增大到最大值.所以这一幅图可以表示我们的第一个栈帧;

地址 名字
0 x 42

我们在地址0的地方分配了一个叫做x的变量,它的值是42.

当foo()函数调用的时候,一个新的栈帧被分配了:

地址 名字
2 z 100
1 y 5
0 x 42

因为0是第一个栈帧,1和2就用于foo()函数的栈帧.当更多的函数被调用的时候,它向上增长.

这个有很重要的事我们先记录一下.数字0,1,3只是为了抽象说明,实际系统中的内存地址和这些数字没有关系.特别的,真实世界的内存地址是被一些字节分开的,这些分开的地址可能比它存放的数要大.

当foo()函数结束的时候,栈帧被释放了:

地址 名字
0 x 42

然后,当main()函数结束的时候,这最后一个值也被释放.简单吧!

栈之所被称为栈,是因为它工作起来就像盘子一样:第一个被放下的盘子在最后才能被拿起来.栈通常被称为"先入后出".你放入的最后一个值会最先被取出来.

让我们是一个3重深度的例子:

fn bar() {

let i = 6;

}

fn foo() {

let a = 5;

let b = 100;

let c = 1;

bar();

}

fn main() {

let x = 42;

foo();

}

ok,首先我们调用main():

地址 名字
0 x 42

然后,main()调用了foo():

地址 名字
3 c 1
2 b 100
1 a 5
0 x 42

foo()又调用了bar():

地址 名字
4 i 6
3 c 1
2 b 100
1 a 5
0 x 42

WOW!我们的栈涨的很高了.

ber()结束以后,它的栈被释放,只剩foo()和main()了:

地址 名字
3 c 1
2 b 100
1 a 5
0 x 42

然后foo()结束后,只剩下了main()函数:

地址 名字
0 x 42

这样就结束了.明白了么?这就像叠盘子:在顶部添加,从顶部拿走.

时间: 2024-11-20 10:01:42

Rust中文翻译12的相关文章

Rust中文翻译31

5.12 结构体 结构体是一个表示复杂数据类型的方式.例如,你可以计算二维坐标,我们需要x和y的值: let origin_x = 0; let origin_y = 0; 一个结构体就可以让你将两个值合并成一个独立的值: struct Point { x: i32, y: i32, } fn main() { let origin = Point {x: 0, y: 0}; // origin: Point println!("The origin is at ({}, {})",

Rust中文翻译19

我们来讨论一下迭代器. 还记得Rust的for循环么?有一个例子: for x in 0..10 { println!("{}", x); } 现在你更了解Rust了,我们可以讨论它的工作细节了.区间(Ranges)(0..10)就是迭代器.一个迭代器可以重复的调用.next()方法,然后给我们返回一个序列. 像这样: let mut range = 0..10; loop { match range.next() { Some(x) => { println!("{}

Rust中文翻译7

去台湾玩儿了一个礼拜,赶紧回来继续翻译吧! 3.1.5 循环 Page 38 loop关键字可以实现一个无限循环.让我们来加入一个循环: extern crate rand; use std::io; use std::cmp::Ordering; use rand::Rng; fn main() { println!("Guess the number!"); let secret_number = rand::thread_rng().gen_range(1, 101); prin

Rust中文翻译29

5.10 生命期 本节是Rust三处描述所有权系统的其中之一.所有权是Rust最独特和引人注目的特性,这也是Rust程序员必须熟悉的一个特性.所有权使Rust得以实现它最大的设计目标,内存安全.这里有一些不同的概念,每一个都有自己的章节: 所有权,你正在读的 借用(borrowing, 5.9), 以及它的关联特性'引用' 生命期(5.10),以及borrowing的高级特性 这三者是相关的,也是循序渐进的.你必须要完全理解这个三个部分. 5.10.1 元 在我们讨论细节之前,有两个关于所有权系

Rust中文翻译21

4.7 错误处理 有时候程序会发生错误.对于不可避免的事情发生时最好有一个计划来处理.Rust有丰富的处理错误的方法. 你的程序会出现两种类型的错误:失败和崩溃.我们先讨论两者的区别,然后讨论如何处理他们.然后,我们讨论把错误升级为崩溃. Page 107 4.7.1 失败和崩溃 Rust使用两种错误的类型:失败和崩溃.失败是一种可以被挽回的错误.崩溃却不行. "挽回"是什么意思呢?在多数情况下,很有可能会发生错误.例如,一个parse函数: "5".parse()

Rust中文翻译28

5.9 引用和借用 本节是Rust三处描述所有权系统的其中之一.所有权是Rust最独特和引人注目的特性,这也是Rust程序员必须熟悉的一个特性.所有权使Rust得以实现它最大的设计目标,内存安全.这里有一些不同的概念,每一个都有自己的章节: 所有权,你正在读的 借用(borrowing, 5.9), 以及它的关联特性'引用' 生命期(5.10),以及borrowing的高级特性 这三者是相关的,也是循序渐进的.你必须要完全理解这个三个部分. 5.9.1 元 在我们讨论细节之前,有两个关于所有权系

Rust中文翻译17

4.2.4 文档测试 没有什么比带有示例的文档更好的了.也没有什么比不能工作的例子更糟的了,因为有可能文档中的代码已经修改了.为此,Rust支持自动测试我们示例代码.让我们看一个富有血肉的src/lib.rs的例子: //! The àdder` crate provides functions that add numbers to other numbers. //! //! # Examples //! //! ``` //! assert_eq!(4, adder::add_two(2)

Rust中文翻译14

Page 71 我们给j,i,h分配了内存.i在堆上,所以是一个指向堆的指针保存在i里.然后再main()函数的结尾,foo()函数被调用: 地址 名字 值 230   20 ... ... ... 5 z 4 4 y 10 3 x 0 2 j 0 1 i 230 0 h 3 x,y,z都得到了各自的内存.参数x拥有和j一样的值,那是因为我们就是这样传参的.它其实是一个指向0地址的指针,因为j指向的是h. 下面,foo()函数调用了baz(),传递了z: 地址 名字 值 230   20 ...

Rust中文翻译15

大多数使用垃圾回收的语言都默认在堆上分配内存.这就意味着每个值都要装箱.有很多原因导致他们这样设计,但是这超出了本书的范围.同样,也有很多优化设计导致它并不是100%这样工作的.垃圾回收器宁愿选择在堆上分配内存,也不使用栈和Drop操作来释放内存. 4.1.7 该用哪一种呢? 所以当栈又快又好用的时候,为什么我们还需要使用堆呢?一个重要的原因是,栈只给你提供了LIFO语义来管理内存.堆分配却可以提供更加通用的和任意的顺序来使用内存,同时附带一些开销. 通常情况下,你应当使用栈,Rust默认就是如