Getting to know the concept of ownership in Rust

At present, I have only read the official documents of the second edition and recorded the initial impression. There should be a more profound and consistent explanation. The level is limited and is for reference only.
Experimental environment : ubuntu17.10, rust1.18, vscode1.14 + extended rust (rls).
BTW, the environment setup is surprisingly smooth, the Rust toolchain is simple and beautiful, supports git natively, and only needs one command to install: curl https://sh.rustup.rs -sSf | sh.

first impression

There are three main conditions for a data race:

  1. Two or more pointers access the same data at the same time.
  2. At least one pointer was written.
  3. There is no mechanism for synchronizing data access.

Rust attaches great importance to concurrency. According to the official introduction: Rust is a programming language focusing on safety, speed and concurrency. And concurrency needs to solve the problem of data competition. Naturally, it will attach great importance to the use of data. It is not an exaggeration to say that. Because the data can only be used when it is associated with a named variable, rust introduces a solution for the use of variables at the language level. The main syntax involved are:

  1. When a variable is defined, immutable (immutable, default ), mutable (mutable)
  2. When assigning a variable, ownership transfer (move), borrow (borrow)

It should be noted that ownership is only for complex type variables (syntactically, types without copy trait), such as String, vect and other types that store data on the heap, while simple types are not considered, such as int, tuple, array etc. The reason is how the data is copied when assigning (although they are all shallow copies).

If you are familiar with the concepts of shallow copy and deep copy, you will naturally understand that for complex types that allocate space on the heap, shallow copy will cause two or more variables/pointers to point to the same data at the same time. If there are variables/pointers for write operations , it will cause a data race problem.
complex type

So Rust breaks data race conditions with mutable/immutable, ownership, lifetimes, etc., and these solutions are all done at compile time!
Of course, the price is that it is difficult to quickly verify the idea. After all, you have to be careful when using variables, otherwise you will not be able to edit it. I look forward to best practices and IDE support.

basic concept

1. Immutable, mutable

let x = 3;  // x 默认不可变
x = 4;  // 错误!
let x = 4;  // 正确!遮盖了原有的同名变量
let mut y = 3;  // y可变
y = 4;  // 正确!

2. Ownership transfer (move)

fn test(v: String) { println!("fn: {}", v); }  // 函数
let x = String::from("hello");  // 所有者x(String类型)
let y = x;  // 数据的所有权转移给y!
let z = x; // 错误!x已不可用
test(y);  // 所有权转移,新的所有者是形参v!当函数执行完毕,v离开作用域时值被丢弃(drop)!
println!("var: {}", y);  // 错误!y已不可用

It must be maddening, can it still be fun to play? This data runs like a rabbit, and when you want to use it, you don't know where it goes! You may also accidentally run into the function and lie down!

3. Borrow/reference (borrow)

So, what if you want to use a variable multiple times? The answer is that it can be borrowed: use its value without taking ownership of it.

fn test1(v: String) { println!("fn: {}", v); } 
fn test2(v: &String) { println!("fn: {}", v); }  // 参数为引用类型
let s = String::from("hello");  // 所有者s(String类型)
let s1 = &s;  // 不可变借用(borrow)!
let s2 = &s;  // 借用
let s3 = s1;  // 借用
test2(s1);  // 借用
test1(*s1);  // 错误!借用者s1没有所有权,无法通过s1转移(cannot move out of borrowed content)。
println!("var: {}", s); // 正确

**Summary:** Personally, ownership transfer is mainly for concurrent services, and it is not commonly used. After all, data is often reused, and no one is willing to keep guarding where the data goes, especially when calling functions. In this case, the owner is generally kept unchanged, and references are used more, which is mainly reflected in complex data structures and functions.

further

However, the actual use situation will be more complicated, that is, whether the variable, transfer and borrowing affect each other (mixed use).
From the perspective of data competition: reading and reading do not conflict, but reading, writing, and writing will conflict (reading is immutable, and writing is variable); from the perspective of implementation: reference is based on ownership.
So, you can see which objects will conflict: (owner, reference) × (immutable, mutable)The relationship between the three from the perspective of realization

  • First, mutable or not has nothing to do with ownership.
let x = String::from("hello");
let mut z = x;  // 转移后变量x不可用
z.push_str(" z");  //正确
// 可变引用要用星号来获得引用的内容,不可变引用不需要。
let mut x = 5; 
let y = &mut x;
*y += 1;
  • Although an immutable reference (&T) has no ownership and will not cause the value to be transferred by mistake, it requires that the value cannot be changed when borrowing , which means that at this time : the ownership cannot be transferred, the owner cannot change the value, and there cannot be mutable references at the same time!
let mut x = String::from("hello");
let y = &x;  // 不可变引用
let z = x;  // 错误
x.push_str(" x");  // 错误
let z = &mut x;  // 错误:可变引用

mutable reference (&mut T)

The use of variable references is slightly complicated, and the concept is not too unified. It is examined separately here.
"Variable rights", that is, the read and write rights of variable references to data , are unique (only one available variable reference) and exclusive (all other reads and writes are invalid), so they have a considerable impact on compilation. The mutable rights of mutable references are similar to the owner's ownership of data, because mutable rights also have move behavior.

**Note:** There is no concept of variable rights in the official documents, but I personally feel that it is better to use this concept to understand the use of variable references, and there may be more essential explanations for reference only.

  • Two ways of variable weight move
let mut x = String::from("hello");  // 所有者x有可变权
 // 1. 直接转移
let y = &mut x;  // 1. y为可变引用,可变权move自x
let z = y;  // 直接转移。z为可变引用
y.push_str(" y");  // 错误!y的可变权已move给z
z.push_str(" z");  // 正确
 // 2. 间接转移
let mut y = &mut x;  // 2. y为可变引用,可变权move自x
let w = &mut y;  // 要求y可变。w为可变引用
w.push_str(" w");  // 正确
// 转移(函数)
fn test(i: &mut String) { 
	i.push_str(" i");   // 正确
}
let mut x = String::from("hello");  // 所有者x有可变权
test(&mut x);
x.push_str(" x");  // 正确!可变权已归还
  • A mutable reference requires a mutable owner if it is written .
let x = String::from("hello");  // x不可变
let mut z = &x;   // z为不可变引用
z.push_str(" z");  // 错误!
let w = &mut z;  // w为可变引用
w.push_str(" w");  // 错误!
let mut y = x;  // 所有权转移,y可变
let z = &mut y;   // z为可变引用,要求y可变
z.push_str(" z");  // 正确!
let w = &z;  // w 为不可变引用
w.push_str(" w");  // 错误!

Summarize:

Because both involve value modification, mutable references behave like owners , and both mutable rights and ownership are data-oriented and unique .

owner

  1. Has ownership, is no longer available after move, and when the owner's lifetime expires, the value is discarded.
  2. Reads like immutable references, and writes like mutable references.

mutable reference (&mut T)

  1. With mutable rights, move from the referee, when the life of the mutable reference ends, the mutable rights are automatically returned.
  2. The source of variable rights should come from the owner, otherwise it is of little significance.

refer to

  1. Rust environment configuration items list
  2. Official Documentation: The Rust Programming Language (Second Edition)
  3. Ownership of Rust Tutorial 11
  4. What pitfalls have you encountered in Rust during development?

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325537641&siteId=291194637