23.所有权.rs

/* 所有权
1.Rust 中的每个值都有一个变量,称为其所有者。
2.一次只能有一个所有者。
3.当所有者不在程序运行范围时,该值将被删除。


变量与数据交互方式主要有移动(Move)和克隆(Clone)两种。可以引用租借

*/

fn main() {

    {
        // 在声明以前,变量 s 无效
        let _s = "runoob";
        // 这里是变量 s 的可用范围
    }
    // 变量范围已经结束,变量 s 无效

    test1();

}




fn test1(){
    let x = 5;
    let y = x;
    println!("{} -- {}",x,y);
    /*
    这个程序将值 5 绑定到变量 x,然后将 x 的值复制并赋值给变量 y。现在栈中将有两个值 5。此情况中的数据是"基本数据"类型的数据,
    不需要存储到堆中,仅在栈中的数据的"移动"方式是直接复制,这不会花费更长的时间或更多的存储空间

    基本数据"类型有这些:
        所有整数类型,例如 i32 、 u32 、 i64 等。
        布尔类型 bool,值为 true 或 false 。
        所有浮点类型,f32 和 f64。
        字符类型 char。
        仅包含以上类型数据的元组(Tuples)。
    */

    
    let x = "12332";
    let y = x;
    println!("{} -- {}",x,y);//有点奇怪,x为啥没有失效?

    /*
    两个 String 对象在栈中,每个 String 对象都有一个指针指向堆中的 "hello" 字符串。在给 s2 赋值时,只有栈中的数据被复制了,堆中的字符串依然还是原来的字符串。
    前面我们说过,当变量超出范围时,Rust 自动调用释放资源函数并清理该变量的堆内存。但是 s1 和 s2 都被释放的话堆区中的 "hello" 被释放两次,这是不被系统允许的。
    为了确保安全,在给 s2 赋值时 s1 已经无效了。没错,在把 s1 的值赋给 s2 以后 s1 将不可以再被使用
    s1 名存实亡。
    */
    let s1 = String::from("hello");
    let s2 = s1;
    //println!("{} -- {}",s1,s2);//这种写法就可以失效了?

    ///克隆
    /*
    Rust会尽可能地降低程序的运行成本,所以默认情况下,长度较大的数据存放在堆中,且采用移动的方式进行数据交互。
    但如果需要将数据单纯的复制一份以供他用,可以使用数据的第二种交互方式——克隆。
    */
    let s1 = String::from("hello");
    let s2 = s1.clone();
    println!("s1 = {}, s2 = {}", s1, s2);

    //涉及函数的所有权机制
    let s = String::from("hello");
    // s 被声明有效

    takes_ownership(s);
    // s 的值被当作参数传入函数
    // 所以可以当作 s 已经被移动,从这里开始已经无效
    // println!("{}",s); 错误!

    let x = 5;
    // x 被声明有效

    makes_copy(x);
    // x 的值被当作参数传入函数
    // 但 x 是基本类型,依然有效
    // 在这里依然可以使用 x 却不能使用 s

    let s1 = gives_ownership();
    // gives_ownership 移动它的返回值到 s1
    println!("{}",s1);

    let s2 = String::from("takes_and_gives_back");
    // s2 被声明有效
    let s3 = takes_and_gives_back(s2);
    // s2 被当作参数移动, s3 获得返回值所有权
    println!("{}",s3);

    //测试引用
    testreference();
    testreference2();
    testreference3();
    testreference4();
    //let reference_to_nothing =  testreference5();
    

}// 函数结束, x 无效, 然后是 s. 但 s 已被移动, 所以不用被释放

//如果将变量当作参数传入函数,那么它和移动的效果是一样的。
fn takes_ownership(some_string: String) { 
    // 一个 String 参数 some_string 传入,有效
    println!("{}", some_string);
} // 函数结束, 参数 some_string 在这里释放

fn makes_copy(some_integer: i32) { 
    // 一个 i32 参数 some_integer 传入,有效
    println!("{}", some_integer);
} // 函数结束, 参数 some_integer 是基本类型, 无需释放


fn gives_ownership() -> String {
    let some_string = String::from("gives_ownership");
    // some_string 被声明有效
    return some_string;
    // some_string 被当作返回值移动出函数
}

fn takes_and_gives_back(a_string: String) -> String { 
    // a_string 被声明有效
    a_string  // a_string 被当作返回值移出函数
}

fn testreference(){

    let s1 = String::from("testreference");
    let s2 = &s1;
    println!("s1 is {}, s2 is {}", s1, s2);

    /*
    & 运算符可以取变量的"引用"。当一个变量的值被引用时,变量本身不会被认定无效。因为"引用"并没有在栈中复制变量的值
    引用不会获得值的所有权。
    引用只能租借(Borrow)值的所有权。
    引用本身也是一个类型并具有一个值,这个值记录的是别的值所在的位置,但引用不具有所指值的所有权:
    */

    let s1 = String::from("calculate_length");
    let len = calculate_length(&s1);
    println!("The length of '{}' is {}.", s1, len);
}

fn calculate_length(s: &String) -> usize {
    s.len()
}

fn testreference2(){
    // let s1 = String::from("hello");
    // let s2 = &s1;
    // let s3 = s1;
    // println!("{}", s2);

    //这段程序不正确:因为 s2 租借的 s1 已经将所有权移动到 s3,所以 s2 将无法继续租借使用 s1 的所有权。如果需要使用 s2 使用该值,必须重新租借:
    let s1 = String::from("testreference2");
    let mut s2 = &s1;
    let s3 = s2;
    s2 = &s3; // 重新从 s3 租借所有权
    println!("{}", s2);
}

fn testreference3(){
    //如果尝试利用租借来的权利来修改数据会被阻止:
    // let s1 = String::from("testreference3");
    // let s2 = &s1; 
    // println!("{}", s2);
    // s2.push_str("oob"); // 错误,禁止修改租借的值
    // println!("{}", s2);

    //当然,也存在一种可变的租借方式,就像你租一个房子,如果物业规定房主可以修改房子结构,房主在租借时也在合同中声明赋予你这种权利,你是可以重新装修房子的

    let mut s1 = String::from("testreference3");
    // s1 是可变的
    let s2 = &mut s1;
    // s2 是可变的引用
    s2.push_str("oob");
    println!("{}", s2);

}

fn testreference4(){
    //可变引用与不可变引用相比除了权限不同以外,可变引用不允许多重引用,但不可变引用可以:
    // let mut s = String::from("hello");
    // let r1 = &mut s;
    // let r2 = &mut s;
    // println!("{}, {}", r1, r2);

    //Rust 对可变引用的这种设计主要出于对并发状态下发生数据访问碰撞的考虑,在编译阶段就避免了这种事情的发生。
    //由于发生数据访问碰撞的必要条件之一是数据被至少一个使用者写且同时被至少一个其他使用者读或写,所以在一个值被可变引用时不允许再次被任何引用。
}

//垂悬引用(Dangling References)
// fn testreference5() -> &String {
//     let s = String::from("hello");
//     &s
//     /*
//     这是一个换了个名字的概念,如果放在有指针概念的编程语言里它就指的是那种没有实际指向一个真正能访问的数据的指针(注意,不一定是空指针,还有可能是已经释放的资源)。它们就像失去悬挂物体的绳子,所以叫"垂悬引用"。
//     "垂悬引用"在 Rust 语言里不允许出现,如果有,编译器会发现它。

//     很显然,伴随着 dangle 函数的结束,其局部变量的值本身没有被当作返回值,被释放了。但它的引用却被返回,这个引用所指向的值已经不能确定的存在,故不允许其出现。
//     */
// }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值