? ? 所有权转移,Rust中没有垃圾收集器,使用所有权规则确保内存安全,所有权规则如下:
? ? 1、每个值在Rust中都有一个被称为其所有者(owner)的变量,值在任何时候只能有一个所有者。
? ? 2、当所有者离开作用域,这个值将被丢弃。
? ? 3、所有权的转移时零成本的,这里不需要对新的变量开辟一块内存用于存储数据。新变量只是重新分配了资源的所有权。
? ? 例子1:所有权传递(变量)
fn main()
{
let x="hello".to_string();//"hello"的所有者为x
let y=x;//"hello"的所有者变为y,这个时候原来的所有者x已失效
println!("{}",x);//
}
例子2:所有权传递(函数)
fn create_string() ->String
{
// 创建并返回一个新的String
String::from("Hell,ownership!")
}
fn transfer_ownership(s:String) -> String
{
//返回输入的String,转移所有权
s
}
fn main() {
//create_string函数创建了一个值"Hell,ownership!",并将所有权传递给了my_string。
let my_string=create_string();
//my_string将值"Hell,ownership!"的所有权传递给了函数transfer_ownership,函数又将所有权传递给了transferred_string
let transferred_string=transfer_ownership(my_string);
//此时my_string已不再对值"Hell,ownership!"拥有所有权。
println!("my_string string: {}", my_string);
// 此时transferred_string对值"Hell,ownership!"拥有所有权。
println!("Transferred string: {}", transferred_string);
}
? ? 克隆:
? ? 1、当克隆一个变量时,相当于创建了数据的一个完整副本。
? ? 2、与所有权转移相比,克隆的成本较大,因为涉及到了要内存的使用和数据的复制。
? ? 3、所有权转移后原始变量失效,克隆之后原始变量仍然有效,并且原始变量保留了数据的所有权。这里需要注意,克隆后,两个变量是完全独立的数据实例。
? ? 例子3:
fn main()
{
let x="Hello".to_string();//x获取了"Hello"的所有权
let y=x.clone();//传递x的副本给y
clone_ownership(y);//传递x的副本y给函数clone_ownership
//x依然对"Hello"拥有所有权
println!("x String:{}",x);
//y对"Hello"还拥有所有权吗?不再拥有所有权了,因为已经将所有权传递给了函数clone_ownership
// println!("y String:{}",y);
}
fn clone_ownership(s:String)
{
println!("{}",s);
}
? ? 引用:
? ? 1、引用有可变引用(&mut T)和不可变引用(&T)。
? ? 2、可变引用允许修改引用所指向的值,而不可变引用不允许修改引用所指向的值。
? ? 3、为了防止数据竞争,Rust中在任何时间,只能拥有一个可变引用到特定的数据。
? ? 4、由于不可变引用不会改变数据,Rust中可以拥有任意数量的不可变引用。
? ? ?例子4:
// 定义一个函数,它接受一个整数的不可变引用
fn print_int(value:&i32)
{
// 打印出传入整数的值
println!("The value is: {}",value);
}
fn main()
{
let _int=11;
// 调用函数,传入整数的不可变引用
print_int(&_int);
}
? ? 例子5:
// 定义一个函数,它接受一个整数的可变引用
fn print_int(value:&mut i32)
{
// 将整数的值加倍
*value *=10;
}
fn main()
{
let mut _int=10;
// 调用函数,传入整数的可变引用
print_int(&mut _int);
// 打印加倍后的整数值
println!("The value is :{}",_int);
}
5、生命周期参数,它可以告诉编辑器,参数的引用和返回值的引用都具有相同的生命周期。例子6中的'a就是生命周期参数
?
例子6
fn return_reference<'a>(data:&'a String)->&'a String
{
data //返回的引用与输入的引用具有相同的生命周期
}
fn main()
{
let external_string=String::from("Hello world");
let string_ref=return_reference(&external_string);
println!("{}",string_ref);// 这是安全的,因为external_string的生命周期贯穿了整个main函数
}
例子7
//定义一个包含引用的结构体,需要生命周期注解
struct Item<'a>
{
//'a表示引用的生命周期
name:&'a str,
}
// 实现结构体,战术如何使用生命周期
impl<'a> Item<'a>
{
// 创建一个新的Item实例,返回一个带有生命周期的实例
fn new(name:&'a str)->Self{
Item {name}
}
}
fn main()
{
let name =String::from("Rust Programming");// 创建一个String类型的变量
let item= Item::new(&name);//借用name的引用来创建Item实例
println!("Item name:{}",item.name);//打印Item中的name字段
}//name的生命周期结束,item.name的引用也不再有效
例子8
fn print_shorter(r:&str)
{
println!("The string is:{}",r);
}
fn main(){
let long_lived_string=String::from("This is a long-lived string.");
{
let short_lived_str:&str=&long_lived_string;//创建一个常生命周期的引用
// 下面的函数调用中,short_lived_str的生命周期会被强制缩短以匹配print_shorter
print_shorter(short_lived_str);
}//short_lived_str的生命周期结束
//这里long_lived_string仍然有效,因此上面的强制转换是安全的
println!("{}",long_lived_string);
}
例子9
fn main()
{
let mut data=vec![1,2,3,4,5];
//创建一个不可变引用
let data_ref=&data;
//打印使用不可变引用的数据
println!("Values via immutable reference:{:?}",data_ref);
// 下面尝试创建一个可变引用将会失败,因为`data`已经被不可变引用借用
let data_mut_ref = &mut data;
println!("{}", data_mut_ref);// 这行会导致编译错误,编译错误如下图
//下面尝试创建一个可变引用将不会失败
// let data_mut_ref=&mut data;
// println!("{:?}",data_mut_ref);
//只有当不可变引用不再使用后,才能创建可变引用
//这里不再使用不可变引用data_ref,因此可以创建可变引用
// let data_mut_ref=&mut data;
// data_mut_ref.push(6);
// println!("Values after mutation:{:?}",data_mut_ref);
}
从错误截图上看,只有使用{:?}编译器错误就不存在了。
例子10:如果有一个或多个不可变引用&T,那么在同一作用域内不能有可变引用&mut T。
如果有一个可变引用&mut T,那么在同一作用域内不能有其他的可变引用或不可变引用&T。
编译器报错提示如下
struct MyStruct{
value:i32,
}
//这个函数尝试同时接受一个对象的可变和不可变引用
fn example_fn(mutable_ref:&mut MyStruct,immutable_ref:&MyStruct){
println!("Mutable reference value:{}",mutable_ref.value);
println!("Immutable reference value:{}",immutable_ref);
}
fn main() {
let mut my_object=MyStruct{value:10};
//尝试同时借用可变引用和不可变引用
example_fn(&mut my_object, &my_object);
}
?
?
例子11:
fn main()
{
let x=10;//定义一个整数变量x
let y=&x;//创建一个指向x的引用y
println!("The value of x is:{}",x);//直接打印变量x的值。
println!("The address of x is:{:p}",y);//打印引用y的地址,使用{:p}格式化指针地址
println!("The value of y is:{}",y);//打印应用y的值,这里会打印出地址 注意这里打印出来的是10而不是地址,不确定是不是Rust版本的问题。
println!("The value pointed to by is :{}",*y);//使用解引用操作符来打印y指向的值
}
例子12
// 定义一个包含字符串引用的结构体Book
struct Book<'a>{
//'a是一个生命周期注解,表示title的生命周期
title:&'a str,
}
fn main(){
let title=String::from("The Rust Programming Language");
let book=Book{
//title 是一个字符串切片,他引用了title变量的数据
title:&title,
};
println!("Book title:{}",book.title);
}
例子13
// longset 函数定义了一个生命周期参数'a,这个生命周期参数制定了输入参数和返回值的生命周期必须相同。
fn longset<'a>(x:&'a str,y:&'a str)->&'a str
{
if x.len()>y.len(){
x// 如果x的长度大于y,返回x
}
else {
y //否则,返回y
}
}
fn main()
{
let string1=String::from("Rust");
let string2=String::from("C++");
let result=longset(string1.as_str(), string2.as_str());//longset函数比较两个字符串切片的长度
println!("The longeset string is {}",result);
//注意 result 的生命周期与string1和string2的生命周期相关,因此它们必须在result被使用之前保持有效
}
例子14
// 定义一个结构体ImportantExcerpt,它包含一个字符串切片字段part
struct ImportantExcerpt<'a>
{
part:&'a str,
}
fn main()
{
let novel=String::from("Call me ishmael. Some years ago ...");
let first_sentence=novel.split('.').next().expect("Could not find a '.'");
let excerpt=ImportantExcerpt{
part:first_sentence,
};
//打印出结构体中的字符串切片
println!("Important excerpt:{}",excerpt.part);
}
例子15
// 定义一个结构体ImportantExcerpt,它包含一个字符串切片字段part
struct ImportantExcerpt<'a>
{
part:&'a str,
}
//为ImportantExcept结构体实现方法
impl <'a> ImportantExcerpt<'a> {
fn announce_and_return_part(&self,announcement:&str)->&str{
println!("Attention please:{}",announcement);
self.part
}
}
fn main()
{
let novel=String::from("Call me ishmael. Some years ago ...");
let first_sentence=novel.split('.').next().expect("Could not find a '.'");
let excerpt=ImportantExcerpt{
part:first_sentence,
};
let announcement="I'm going to tell you something important!";
let part=excerpt.announce_and_return_part(announcement);
//打印出结构体中的字符串切片
println!("Important excerpt:{}",part);
}
例子16
//定义一个拥有静态生命周期的字符串常量
static MESSAGE:&'static str="Hello,this is a static lifetime example";
fn main()
{
//打印这个静态生命周期的字符串
println!("{}",MESSAGE);
}
例子17
//定义一个函数,该函数接收两个引用参数:一个是不带生命周期的引用,另一个是带生命周期的'a的引用
fn select<'a>(first:&i32,second:&'a i32)->&'a i32{
//这里我们简单地返回第二个参数,它带有生命周期'a
second
}
fn main()
{
let num1=10;
let num2=20;
//创建一个生命周期较长的引用
let result;
{
let num3=num2;
//调用函数,num1的引用不带生命周期,num3的引用带有生命周期
result=select(&num1, &num3);
}//num3的生命周期结束
//打印结果,result引用的是num2,因为它与num3共享相同的数据
println!("The selected number is {}",result);
}
例子18
//定义一个结构体Book,包含一个字符串切片引用,代码书名
struct Book<'a>{
name:&'a str,
}
//实现Book结构体的一个方法get_name,返回书名的引用
//这里没有显示标注生命周期,因为编译器会自动应用生命周期省略规则
impl<'a> Book<'a>
{
//根据省略规则,这里的返回值生命周期被自动推导问为与&self相同
fn get_name(&self)->&str{
self.name
}
}
fn main()
{
let book=Book{name:"The Rust Programming Language"};
//调用get_name方法,打印返回的书名引用
println!("Book name:{}",book.get_name());
}
例子19
// 定义一个泛型函数`slice_first`,它有一个泛型类型`T`和生命周期`'a`
fn slice_first<'a, T>(data: &'a [T]) -> Option<&'a T> {
// 使用`.get()`方法来尝试获取slice的第一个元素的引用
// 如果存在,则返回Some(&T),否则返回None
data.get(0)
}
fn main() {
// 创建一个整数类型的slice
let numbers = vec![1, 2, 3, 4, 5];
// 调用`slice_first`函数,并打印返回的结果
if let Some(first) = slice_first(&numbers) {
println!("The first number is {}", first);
} else {
println!("The slice is empty.");
}
// 创建一个字符类型的slice
let letters = vec!['a', 'b', 'c', 'd', 'e'];
// 同样调用`slice_first`函数,并打印返回的结果
if let Some(first) = slice_first(&letters) {
println!("The first letter is {}", first);
} else {
println!("The slice is empty.");
}
}