Rust中的盒子和指针 (zz)

 

 


 

 

智能指针是一种数据结构,其行为类似于指针,同时提供内存管理或绑定检查等附加功能。
智能指针可跟踪其指向的内存,还可用于管理其他资源,如Fils句柄和网络连接。
智能指针最初用于C++语言。
引用也是一种指针,但除了引用数据之外,它没有其他功能。引用由&运算符表示。
智能指针提供的功能超出了参考提供的功能。智能指针提供的最常见功能是“引用计数智能指针类型”。此功能能够通过跟踪所有者来拥有多个数据所有者,如果没有所有者,则可以清除数据。
引用是仅借用数据的指针,而智能指针是拥有它们指向的数据的指针。

智能指针的类型:

Rust中的盒子和指针 (zz)

 

  • Box <T>:Box <T>是一个智能指针,指向在类型为T的堆上分配的数据,其中“T”是数据的类型。它用于将数据存储在堆上而不是堆栈上。
  • Deref <T>:Deref <T>是一个智能指针,用于自定义解除引用运算符(*)的行为。
  • Drop <T>:Drop <T>是一个智能指针,用于在变量超出范围时从堆内存中释放空间。
  • Rc <T>:Rc <T>代表参考计数指针。它是一个智能指针,用于记录存储在堆上的值的引用数。
  • RefCell <T>:RefCell <T>是一个智能指针,允许借用可变数据,即使数据是不可变的。这个过程被称为内部可变性。

原文出自【易百教程】,商业转载请联系作者获得授权,非商业转载请保留原文链接:https://www.yiibai.com/rust/rust-smart-pointers.html

 

 


Rust中的盒子和指针 
根据Rust 0.5的tutorial整理,指针这部分内容应该不会变化了。 
(豆瓣的帖子没法排版。排版好的请参考http://corwindong.blogspot.com/2013/01/rust_7.html

大多数现代语言对于聚合类型(如class, struct, enum)都采用一种“uniform representation”方式表示,将这些类型缺省表示为分配在堆上的内存的指针。Rust则不同,类似于C和C++,Rust直接表示这些类型。也就是说聚合数据在Rust中是未打包的(unboxed)。这意味着当你写下let x = Point {x: 1f, y: 1f};时,你是在栈上创建了一个struct。如果你把它拷贝到一个数据结构中,你拷贝的是整个struct,而不是指针。 

对于Point这样的小型结构来说,直接拷贝要比先分配内存后再通过指针访问高效。但是对于大型结构,或者那些有可变数据域的结构,只在栈上或堆上保留一份数据,然后通过指针引用会更好一些。 

Rust支持好几种指针(三种)。安全指针@T代表分配在本地堆上的managed boxes;指针~T代表分配在交换堆上的uniquely-owned boxes;而指针&T,代表 borrowed pointers,这种指针可以指向任何内存,同时它们的生命周期由调用栈管理。 

所有的指针类型都由一元运算符* 来解除引用。 

1 Managed Boxes 
Managed Boxes是一种指针,指向堆上分配,具有GC的内存。将一元运算符@ 应用于表达式就建立了一个managed box。生成的盒子包含了表达式的结果。拷贝一个managed box,只是拷贝了指针,而不是盒子中的内容。 

let x: @int = @10; // New box 
let y = x; // Copy of a pointer to the same box 

// x and y both refer to the same allocation. When both go out of scope 
// then the allocation will be freed.  

一个managed type要么形如@T,要么是任何包含了managed boxes或者其他managed types的类型。 

// A linked list node 
struct Node { 
mut next: MaybeNode, 
mut prev: MaybeNode, 
payload: int 


enum MaybeNode { 
SomeNode(@Node), 
NoNode 


let node1 = @Node { next: NoNode, prev: NoNode, payload: 1 }; 
let node2 = @Node { next: NoNode, prev: NoNode, payload: 2 }; 
let node3 = @Node { next: NoNode, prev: NoNode, payload: 3 }; 

// Link the three list nodes together 
node1.next = SomeNode(node2); 
node2.prev = SomeNode(node1); 
node2.next = SomeNode(node3); 
node3.prev = SomeNode(node2); 

Managed Boxes绝不跨越task边界。 

2 Owned Boxes 
和managed boxes不同的是,owned boxes具有内存独享性,因此两个owned boxes不会指向同一内存。所有跨越全部tasks边界的owned boxes都在一个exchange heap上分配。而它们的唯一所有权特性使得tasks可以高效地交换它们。 

由于owned boxes的唯一所有特性,拷贝它们则需要分配一个新的owned box然后复制内容。然而,owned boxes默认使用moved,交换内存所有权,反初始化前一个owning变量。任何企图在变量被move后访问该变量的操作都将触发编译错误。 

let x = ~10; 
// Move x to y, deinitializing x 
let y = x; 

如果你打算拷贝owned box,你必须明确指出: 

let x = ~10; 
let y = copy x; 

let z = *x + *y; 
assert z == 20; 

当owned boxes不包含任何managed boxes时,可以发送给其他task。发送方task将交出box的所有权,发送后将再不能访问它。接收方task将成为box的唯一所有者。 

3 Borrowed Pointers 
Rust的borrowed pointers是通用的引用/指针类型,类似C++的引用类型,但是保证指向有效内存。和owned boxes不同的是,borrowed pointers从不隐含内存所有权。指针可以从任意类型借来,同时保证指针不会比它指向的值生存得更久。 

举个例子,比如一个简单的结构类型:Point 

struct Point { 
x: float, y: float 


我们使用这个简单的类型来展示指针的多种分配方式。例如,在下面的代码中,三个局部变量都包含一个point,但是分配在不同的位置上。 

let on_the_stack : Point = Point {x: 3.0, y: 4.0}; 
let managed_box : @Point = @Point {x: 5.0, y: 1.0}; 
let owned_box : ~Point = ~Point {x: 7.0, y: 9.0}; 

假如我们想要写一个计算两个点距离的函数,要求不管两个点存储在什么位置都能用。例如,我们可能计算on_the_stack和managed_box之间的距离,或者managed_box和owned_box之间的距离。一种方法是定义两个参数都是Point类型的函数,该函数使用point的值。这就会造成我们调用函数时拷贝point的值。对于points而言,问题还不严重,但是一般情况下拷贝都是代价高昂的,甚至更糟的是,存在可变数据域,这些将改变程序的语意。所以我们一般定义接受指向points的指针的函数。我们使用borrowed_pointers来达成目标: 

fn compute_distance(p1: &Point, p2: &Point) -> float { 
let x_d = p1.x - p2.x; 
let y_d = p1.y - p2.y; 
sqrt(x_d * x_d + y_d * y_d) 


现在我们可以以各种方式调用compute_distance(): 

compute_distance(&on_the_stack, managed_box); 
compute_distance(managed_box, owned_box); 

这里的&运算符用来取得变量on_the_stack的地址;我们称这个为borrowing局部变量on_the_stack,因为我们创建了一个别名:也就是访问同一数据的另一种方式。 

而对于像managed_box和owned_box这样的盒子,则不需要什么操作。编译器能自动将类似@point或~point这样的盒子转换为类似&point这样的borrowed pointer。这是另一种形式的borrowing;这种情况下,managed/owned box的内容被借出。 

不论一个值何时被借出,对于原始值得操作都存在一些限制。例如,如果一个变量的内容已经被借出,那么我们不能将该变量发送给别的task,也不能执行任何会使得借出的值被释放,或者类型改变的操作。请牢记这条原则:你必须等借出的值被归还后(也就是等borrowed pointer离开作用域),才能再次对它为所欲为。 

对于borrowed pointers更进一步的解释,请参考borrowed pointer tutorial。 

4 Dereferencing pointers 
Rust使用一元运算符* 来获取box或者pointer的值,这点和C类似。 

let managed = @10; 
let owned = ~20; 
let borrowed = &30; 

let sum = *managed + *owned + *borrowed; 

对可变指针解引用可以出现在赋值式的左边。这样的赋值将修改指针指向的值。 

let managed = @mut 10; 
let owned = ~mut 20; 

let mut value = 30; 
let borrowed = &mut value; 

*managed = *owned + 10; 
*owned = *borrowed + 100; 
*borrowed = *managed + 1000; 

指针相关操作符具有高优先级,但是比取数据域和调用方法的点操作优先级低。 

let start = @Point { x: 10f, y: 20f }; 
let end = ~Point { x: (*start).x + 100f, y: (*start).y + 100f }; 
let rect = &Rectangle(*start, *end); 
let area = (*rect).area(); 

为了消除上面那丑陋的星号括号,点操作符对接受者(点号的左边)自动解引用,所以大多数情况下,不需要对接受者显式解引用。 

let start = @Point { x: 10f, y: 20f }; 
let end = ~Point { x: start.x + 100f, y: start.y + 100f }; 
let rect = &Rectangle(*start, *end); 
let area = rect.area(); 

来个极端的例子,让编译器对任意重指针自动解引用: 

let point = &@~Point { x: 10f, y: 20f }; 
io::println(fmt!("%f", point.x)); 

索引操作符([ ])也具有自动解引用的特性。

 

上一篇:FB商务管理平台(Business Manager) (2)


下一篇:spring-声明式事务