所有权是Rust编程语言提供的独特功能,可在不使用垃圾收集器或指针的情况下提供内存安全保障。
什么是所有权?
当代码块拥有资源时,它被称为所有权。 代码块创建一个包含资源的对象。 当控件到达块的末尾时,对象将被销毁,资源将被释放。
所有权的重点:
- “所有者”可以根据可变性改变变量的拥有值。
- 所有权可以转移到另一个变量。
- 所有权只是在Rust中移动语义。
- 所有权模型也保证了并行的安全性。
所有权规则
- 在Rust中,每个值都有一个与之关联的变量,并称为其所有者。
- 一次只能有一个所有者。
- 当所有者超出范围时,与其关联的值将被销毁。
所有权的例子
多个变量可以在Rust中相互交互。 下面来看一个例子:
将x
的值赋给变量y
:
let x=10;
let y=x;
在上面的例子中,x
绑定到值10
。然后,x
的值被赋给变量y
。 在这种情况下,不会创建x
的副本,而是将x
的值移动到变量y
。 因此,x
的所有权被转移到变量y
,并且变量x
被销毁。 如果尝试重用变量x
,那么Rust会抛出一个错误。可通过下面的例子来理解这一点。
fn main()
{
let x=10;
let y=x;
println!("value of x :{}",x);
}
编译上面代码,示例的输出如下:
内存和分配
在Rust中,数据可以存储在堆栈或堆内存中。
堆栈存储器: 在堆栈存储器中,数据始终按顺序放置并以相反的顺序移除。 它遵循“后进先出”的原则,即始终首先删除最后插入的数据。 堆栈内存是一种有组织的内存。 它比堆内存更快,因为它访问内存的方式。 如果在编译时数据的大小未知,则堆内存用于存储内容。
堆内存: 堆内存是有组织的内存。 操作系统在堆内存中找到一个空的空格并返回一个指针。 此过程称为“在堆上分配”。
此图显示堆栈包含指针,而堆包含内容。
下面来看一个简单的内存分配示例。
fn main()
{
let v1=vec![1,2,3];
let v2=v1;
}
第1步:
在程序的第一个语句中,向量v1
与值1
,2
和3
绑定。向量由三部分组成,即指向存储器中指向存储在内存中的数据的指针,长度和向量的容量。 这些部分存储在堆栈中,而数据存储在堆内存中,如下所示:
第2步:
在程序的第二个语句中,将v1
向量分配给向量v2
。 指针,长度和容量将复制到堆栈中,但不会将数据复制到堆内存中。现在内存的表示如下:
但是,这种表示形式可能会产生问题。 当v1
和v2
都超出范围时,两者都会尝试释放内存。 这会导致双重空闲内存,这会导致内存损坏。
第3步:
Rust避免了第2步 条件以确保内存安全。 Rust没有复制分配的内存,而是认为v1
向量不再有效。 因此,当v1
超出范围时,它不需要释放v1
的内存。
使用复制特征
复制特征是一种特殊的注释,它放在存储在堆栈上的整数类型上。 如果在类型上使用了复制特征,则即使在赋值操作之后也可以进一步使用旧变量。
以下是一些复制类型:
- 所有整数类型,如
u32
。 - 布尔类型-
bool
,其值为true
或false
。 - 所有浮动类型,如
f64
。 - 字符类型,如
char
。
所有权和函数
当变量传递给函数时,所有权将移动到被调用函数的变量。 传递值的语义等于为变量赋值。
下面通过一个例子来理解这一点:
fn main()
{
let s=String::from("Yiibai");
take_ownership(s);
let ch='a';
moves_copy(ch);
println!("{}",ch);
}
fn take_ownership(str:String)
{
println!("{}",str);
}
fn moves_copy(c:char)
{
println!("{}",c);
}
执行上面示例代码,得到以下结果 -
Yiibai
a
a
在上面的例子中,字符串s
与值Yiibai
绑定,s
变量的所有权通过take_ownership()
函数传递给变量str
。 ch
变量与值a
绑定,ch
变量的所有权通过moves_copy()
函数传递给变量c
。 之后也可以使用ch
变量,因为此变量的类型是“复制”特征。
返回值和范围
从函数返回值也会转移所有权。看看这个示例:
fn main()
{
let x= gives_ownership();
println!("value of x is {}",x);
}
fn gives_ownership()->u32
{
let y=100;
y
}
执行上面示例代码,得到以下结果 -
value of x is 100
在上面的例子中,give_ownership()
函数返回y
的值,即100
,y
变量的所有权被转移到x
变量。