rust学习(四)所有权

(我觉得所有权这节主要是概念,极少数才是语法,个人觉得用处不大,简洁描述,作用域就是变量在{}的范围内,就有作用,出{}会释放内存;所有权就是能否将变量本身(地址、长度、内容【原文说是容量,我觉得称呼为内容更合适】)返回,能返回就是拥有了变量的所有权;还有一些其他使用方法,感兴趣可以看一下)

所有权,让Rust无需垃圾回收,即可保证内存安全

(4.1)所有权

  • Rust 中的每一个值都有一个被称为其 所有者 的变量。
  • 值在任一时刻有且只有一个所有者。
  • 当所有者(变量)离开作用域,这个值将被丢弃。

1.栈与堆

1.1栈:以放入值的顺序存储值并以相反顺序取出值,即后进先出,栈中所有数据都必须占用已知且固定的大小。

1.2堆:无序,当向堆放入数据时,要请求一定大小的空间,堆中数据与栈相反,不固定、大小未知

2.变量作用域:一个项在程序中有效的范围。(项所在的{}的范围内,即为它的作用域)

3.String类型:

3.1字符串字面量String:被强制编码进程序里的字符串值,它们在定义后不可变。

3.2堆上String:创建:let s = String::from("hello");(“::”是运算符,允许将特定的from函数置于String类型的命名空间下)

fn main() {
    let mut s = String::from("hello");//String::from,在堆上请求内存
    // 定义s为堆上String类型,内容为hello
    s.push_str(", world!"); 
    // push_str() 在字符串后追加字面值
    println!("{}", s); 
    // 将打印 `hello, world!`
}

4.内存与分配

4.1内存

请求的内存,在作用域结束后,将被释放;

4.2(堆上)移动:移动赋值:不复制内容

fn main() {
    let s1 = String::from("hello");
    let s2 = s1;//s2复制s1的指针、长度、地址,同时释放s1
    /*内存中,s2中复制的是s1的指针(地址)、长度、容量,而不是复制内容,
    即s1、s2指向同一位置的数据*/
    
}

4.3(堆上)克隆:clone():克隆赋值,复制一切

fn main() {
    let s1 = String::from("hello");
    let s2 = s1.clone();//将s1克隆到s2

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

4.4栈上拷贝

fn main() {
    let x = 5;
    let y = x;

    println!("x = {}, y = {}", x, y);
}

即堆上旧变量在移动赋值给其他变量后会被释放,而栈上的旧变量在赋值给其他变量后,旧变量仍可使用

5.所有权与函数:通过函数操作堆上、栈上赋值,旧变量什么时候消失,还是遵照:堆上旧变量在移动赋值给其他变量后会被释放,而栈上的旧变量在赋值给其他变量后,旧变量仍可使用。

fn main() {
  let s = String::from("hello");  // s 进入作用域
  takes_ownership(s);             // s 的值移动到函数里 ...
                                  // ... 所以到这里不再有效
  let x = 5;                      // x 进入作用域
  makes_copy(x);                  // x 应该移动函数里,
                                  // 但 i32 是 Copy 的,所以在后面可继续使用 x
} // 这里, x 先移出了作用域,然后是 s。但因为 s 的值已被移走,
  // 所以不会有特殊操作
fn takes_ownership(some_string: String) { // some_string 进入作用域
  println!("{}", some_string);
} // 这里,some_string 移出作用域并调用 `drop` 方法。占用的内存被释放
fn makes_copy(some_integer: i32) { // some_integer 进入作用域
  println!("{}", some_integer);
} // 这里,some_integer 移出作用域。不会有特殊操作

6.返回值与作用域

6.1转移返回值的所有权

(下面示例和中文版不太符合,我复制过来编译,提示出错,改完后才能编译,更改了以下:s1改为_s1;s2改为_s2;就能编译了)

fn main() {
    let _s1 = gives_ownership();         // gives_ownership 将返回值
                                        // 移给 s1
    let s2 = String::from("hello");     // s2 进入作用域
    let _s3 = takes_and_gives_back(s2);  // s2 被移动到
                                        // takes_and_gives_back 中,
                                        // 它也将返回值移给 s3
  } // 这里, s3 移出作用域并被丢弃。s2 也移出作用域,但已被移走,
    // 所以什么也不会发生。s1 移出作用域并被丢弃
  fn gives_ownership() -> String {           // gives_ownership 将返回值移动给
                                             // 调用它的函数
    let some_string = String::from("yours"); // some_string 进入作用域
    some_string                              // 返回 some_string 并移出给调用的函数
  }
  // takes_and_gives_back 将传入字符串并返回该值
  fn takes_and_gives_back(a_string: String) -> String { // a_string 进入作用域
    a_string  // 返回 a_string 并移出给调用的函数
  }

6.2返回参数的所有权

使用元组返回参数的所有权;需要将hello返回调用函数,才能使用hello。

fn main() {
    let s1 = String::from("hello");
    let (s2, len) = calculate_length(s1);
    println!("The length of '{}' is {}.", s2, len);
}
fn calculate_length(s: String) -> (String, usize) {
    let length = s.len(); // len() 返回字符串的长度
    (s, length)
}

(4.2)引用与借用(可变引用)

  • 在任意给定时间,要么 只能有一个可变引用,要么 只能有多个不可变引用。
  • 引用必须总是有效的。

1.引用:只传递地址,但无所有权;调用函数,运行时,s访问s1再访问对应的内容(和c的指针类似)

fn main() {
    let s1 = String::from("hello");
    let len = calculate_length(&s1);
    println!("The length of '{}' is {}.", s1, len);
}
fn calculate_length(s: &String) -> usize {
    s.len()
}

2.可变引用:即借用:在同一时间,只能有一个对某一特定数据的可变引用

fn main() {
    let mut s = String::from("hello");
    change(&mut s);
}
fn change(some_string: &mut String) {
    some_string.push_str(", world");
}

3.悬垂引用:自定义函数创建的变量,需返回变量,而非返回地址

fn main() {
    let reference_to_nothing = dangle();
}
fn dangle() -> &String {
    let s = String::from("hello");
    s//自定义函数返回s,
//还是所有权问题,s在自定义函数结束后,被释放,因此需返回具体的值
}

(4.3)切片Slice类型:允许你引用集合中一段连续的元素序列,而不用引用整个集合

fn main() {
    let s = String::from("hello world");//相当于数组

    let hello = &s[0..5];
    let world = &s[6..11];
}

字符串字面量String的类型为&str,&str不可变,所以字符串字面量类型不可变

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值