Rust:所有权与函数

所有权是Rust最独特的功能,其令Rust无需垃圾回收即可保障内存安全

所有权与函数传参

fn main() {
    let s = String::from("Hello world");   // s 进入作用域
    takes_ownership(s);   // 所有权系统将s的值交给s_str,然后s就失效了,此后不能在访问s
   // println!("{}", s);   //error[E0382]: borrow of moved value: `s`
   //String是堆上的数据,因此有所有权系统管理。

    let x = 5;     // x 进入作用域
    makes_copy(x);  // i32 是 Copy 的,所以在后面可继续使用 x:xx将x绑定的值复制一份,并且绑定到s_int变量中
    println!("{}", x);
}  //这里, x 先移出了作用域,然后是 s。但因为 s 的值已被移走,所以不会有特殊操作

fn takes_ownership(s_str:String){// some_string 进入作用域
    println!("{}", s_str);
}  // 这里,some_string 移出作用域并调用 `drop` 方法。占用的内存被释放

fn makes_copy(s_int:i32){   // some_integer 进入作用域
    println!("{}", s_int);
}  // 这里,some_integer 移出作用域。不会有特殊操作

总结:在堆上的数据由所有权系统管理,函数传参时所有权系统会将值的所有权交给被调用函数的形参,此时,原先绑定值变量失去了自己的值,就失效了
在栈上的数据传值时是传副本,原先绑定值变量并没有失去自己的值,依然可以被访问

不可变变量和不可变引用

1、不可变变量不可以修改所绑定的值,不可变引用只能向第一个人借值,如果借到了就不能再向其他人借值了。

fn main() {
    let s1 = String::from("Hello");  //s1进入作用域

    let s = &s1;   //引用s1:创建一个栈空间s,指向s1,也就是s里面存储s1的地址

    let len = calculate_length(s);  //s是copy的。将s的值也就是s1的地址拷贝一份,并且绑定到 calculate_length的形参s上

    println!("the length of '{}' is {}", s1, len);   //the length of 'Hello' is 5
    println!("{}", s)  //Hello
}

fn calculate_length(s:&String)->usize{   //s是对String的引用
  //    s.push_str(", world"); error   //因为s实质是对不可变变量的引用,因此不允许改变其值
    s.len()
}  //当形参s离开当前作用域时,它绑定的值也就是String的地址被丢弃,但是不影响实际的String,其有有权没有被变更过

我们将获取引用作为参数参数称为借用。 正如现实生活中,如果一个人拥有某样东西,你可以从他那里借来。当你使用完毕,必须还回去。
默认不允许修改借来的值
规则:默认不允许修改引用的值

fn main() {
    let  s1 = String::from("Hello");
    
    change(&s1);
    
    println!("s1 = {} ", s1);
}

fn change(s:& String){
   // s.push_str(" world")  error     //因为s实质是对不可变变量的引用,因此不允许改变其值
    println!("s = {} ", s);
}

在这里插入图片描述

fn main() {
    let  s1 = String::from("Hello");  //s1不可变

    let r = & s1;
    
    let  s2 = String::from("Hello");

  //  r = & s2;  error:r是不可变的
}

不可变变量和不可变引用皆不可改变

2、不可变变量允许多个不可变引用同时借值,反正大家都不能改变这个值–>全部干看着吧

fn main() {
    let  s1 = String::from("Hello");   //s1不可变

    let r1 = &s1;
    let r2 = &s1;

    println!("{}, {} ", r1, r2);  //Hello
}

3、不可变变量可以借值给可变引用和不可变引用,但是不可变变量只有一种借值方法:就是不允许可变引用和不可变引用修改这个值。

可变变量和不可变引用

可变变量有两种借值方法:允许改变其值或者不允许改变其值.

不允许借值者修改值

fn main() {
    let mut s1 = String::from("Hello");   //s1可变
    s1.push_str(" rust");

    let r1 = &s1;

    nochange(r1);
    println!("{}", r1);  //Hello rust
}

fn nochange(r:&String){
  //  r.push_str("world"); //error[E0596]: cannot borrow `*r` as mutable, as it is behind a `&` reference
    println!("{}", r) //Hello rust
}

给借值者修改值的权限

fn main() {
    let mut s1 = String::from("Hello");   //s1可变
    s1.push_str(" rust");

    let r1 = &mut s1;

    nochange(r1);
    println!("{}", r1);  //Hello rustworld
}

fn nochange(r:&mut String){
    r.push_str("world"); 
    println!("{}", r) //Hello rustworld
}

不可变变量与可变引用

可变引用可以多个人借钱,包括可变变量和不可变变量[任意顺序]

fn main() {
    let  s1 = String::from("Hello");   //s1不可变
    
    let mut r = & s1;  //r是可变的

    println!("{} ", r);  //Hello 

    let  s2 = String::from("world");

    r = & s2; 

    println!("{}", r);   //"world"
}

一个可变引用向不可变变量借值,可变引用没有权利对这个值做出改变

fn main() {
    let  s1 = String::from("Hello");   //s1不可变

    let mut r = & s1;  //r是可变的: warning: variable does not need to be mutable。help: remove this `mut`
     try_change(r);

    println!("{} ", r);  //Hello
}

fn try_change(r:&String){
    println!("{}",r);
}

可变变量和可变引用

fn main() {
    let mut s1 = String::from("Hello");

    change(&mut s1);

    println!("s1 = {} ", s1);

}

fn change(s:&mut String){
    s.push_str(" world") ;  //因为s实质是针对一块可变变量的引用
}

总结:mut给予了权限:允许你改变借来的东西

总结:
1、可变引用可以向多个人[变量]借钱[值],不可变引用只能向一个人[变量]借钱[值],当不可变引用第一次借到钱[值]之后,就不能在像其它[变量]人借钱[值]
可变引用可以向不可变变量和可变变量借值:

  • 不可变变量–>可变引用:可变引用无权改变值,不可变变量不可以改变值。此时可变引用实质应该写成不可变引用,也就是应该为不可变变量–>不可变引用
  • 可变变量->可变引用:可变引用有权改变其值,可变变量可以改变值
  • 不可变变量–>不可变引用&可变变量->不可变引用:不可变引用可以向可变变量或者不可变变量借钱,反正都不能修改,但是不可变引用只允许向一个变量借钱,如果已经借到了,就不能像其他人借钱了。区别在于可变变量可以改变值,不可变变量不可以改变值

其他:可变变量的原则(。・∀・)ノ

编写一个函数,该函数接收一个字符串,并返回在该字符串中找到的第一个单词。如果没有找,返回空

fn main() {
    let  s = "string is composed of char".to_string();
    //s.clear();  //cannot borrow `s` as mutable, as it is not declared as mutable:s是不可变的,因此不能调用这个方法,如果想要调用这个方法,必须let mut s

    let res = first_word(&s); //获取 &String 而不是 String,没有得到值得所有权

    println!("{}", res);
}

fn first_word(s:&String)->&str{
    for (i, &item) in s.as_bytes().into_iter().enumerate(){
        if item == b' '{  //空格是一个char,因此需要' '
            return &s[0..=i]; //返回一个引用,这个引用指向传入得那一片内存得一小块[ptr相同但是len和cap不相同]
        }
    }
    return ""
}

2、可变变量的值如果曾经以不可变权限借值出去的话,就不能再以可变权限借值出去
【第一次借值出去是不允许别人修改,那么这个值就是不能再让别人修改了】—>真有原则
当然,如果先前给予别人修改值得权限,再次借给别人得时候可以收回值得权限,但是已经收回值得权限了,就不能再给人修改值得权限了

fn main() {
    let mut  s = "string is composed of char".to_string();
    
    s = "123 456".to_string();  //可以修改
    
    let res = first_word(&s); //获取 &String 而不是 String,没有得到值得所有权

     s = "123".to_string();//error[E0506]: cannot assign to `s` because it is borrowed:值被借出去了,还没有还回来,因此不允许修改
    s.clear();  //cannot borrow `s` as mutable because it is also borrowed as immutable:说了不许修改了,以后
    
    println!("{}", res);
}

fn first_word(s:&String)->&str{
    for (i, &item) in s.as_bytes().into_iter().enumerate(){
        if item == b' '{  //空格是一个char,因此需要' '
            return &s[0..=i]; //返回一个引用,这个引用指向传入得那一片内存得一小块[ptr相同但是len和cap不相同]
        }
    }
    return ""
}

总结:不能在相同作用域中同时存在可变和不可变引用的规则。。推测为什么要这么设计:

fn main() {
   let mut v = vec![1, 2, 3, 4, 5];
    //不可变引用
    let first = &v[0];  
    //可变引用
    v.push(6);  // cannot borrow `v` as mutable because it is also borrowed as immutable

    println!("The first element is: {}", first);
}

vector的结尾增加新元素时,在没有足够空间将所有元素依次相邻存放的情况下,可能会要求分配新内存并将老的元素拷贝到新的空间中,然后释放旧空间。这时,第一个不可变引用就指向了被释放的内存,造成野指针

3、&str是一个指向二进制程序特定位置的slice。如果有一个String,可以传递整个String的slice。定义一个获取字符串slice而不是String引用的函数可以使API更加通用。
enumerate包装iter的结果并且返回一个元组,其中每一个元素是元组的一部分。enumerate返回元组的第一个元素是索引,第二个元素是集合中元素的引用。

fn main() {
    let  s = "string is composed of char".to_string();
        
    let res = first_word(&s[..]); //获取 &String 而不是 String,没有得到值得所有权

    println!("{}", res);
}

fn first_word(s:&str)->&str{
    for (i, &item) in s.as_bytes().into_iter().enumerate(){
        if item == b' '{  //空格是一个char,因此需要' '
            return &s[0..=i]; //返回一个引用,这个引用指向传入得那一片内存得一小块[ptr相同但是len和cap不相同]
        }
    }
    return ""
}

所有权与返回值

fn main() {
    let s1 = gives_ownership();  //gives_ownership()返回一个值,这个值的所有权将转交给s1
    let s2 = String::from("Hello");
    let s3 = takes_and_gives_back(s2);  //s2所绑定的值的的所有权转交给了形参a_string,此后s2失效,不可以再访问。s3获取到值得所有权

    //println!("{}", s2); //error[E0382]: borrow of moved value: `s2`
    println!("{}, {}", s1, s3);
}   //s3移除作用域其值将通过drop被清理掉,s2已经被移出了因此什么操作也不会发生。s1移出作用域,其值将通过drop被清理掉

fn gives_ownership()->String{
    let s_str = String::from("Hello");  //s_str进入作用域
    s_str 
}//将s_str的值转给调用的函数,s_str没有了绑定的值,s_str失效。

fn takes_and_gives_back(a_string:String)->String{   //a_string进入作用域
    a_string  //将a_string绑定的值转交给调用的函数
}

一块内存的所有者只能有一个
变量的所有权:将值赋给另一个变量时移动它。当持有堆中数据值得变量离开作用域时,其值将通过drop被清理掉,除非数据被移动为另一个变量所有。

参考:https://kaisery.github.io/trpl-zh-cn/ch04-01-what-is-ownership.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值