The ownership of

The concept of ownership is one of the biggest differences between Rust and other programming languages. The design of rust is based on ownership. Learning ownership requires repeated practice and experience of design.

  • Each value in Rust has a corresponding variable as its owner.
  • Values have one and only one owner at a time.
  • When the owner leaves its scope, the value it holds is released.

Variable scope

  • X becomes valid once it enters scope.
  • It will remain valid until it leaves scope.
fn main() {
  println!("{}", x); // Error: x has not been initialized
  { // Here is the start of a scope
    let x = "string";
    println!("{}", x); // "string"
  } // Here is the end of a scope, x will be recycled
  println!("{}", x); // Error: x does not exist
}
Copy the code

Type String

Heap storage and stack storage of strings

Initialize a string stored in the heap:

// Create an empty string
let mut s1 = String::new();

// Create a string from a literal
let mut s2 = String::from("hello");

// Append a single char
s1.push('h');

// You can append a string
s2.push_str("world");

println!("{} - {}", s1, s2); // h-helloworld
Copy the code

Initialize a string on the stack:

// The string declared by the literal is stored on the stack and is immutable
let mut x = "string";
x.push('x'); // Error: 'push' method does not exist
Copy the code

Transfer of ownership

Data stored in the heap is subject to a transfer of ownership:

// Initialize a string in the heap
let s1 = String::from("1");

// the pointer to s1 is shifted to s2, and s1 will be released
let s2 = s1;
println!("{}", s2) / / "1"

// Try to get s1
println!("{}" s1) // The data in S1 has been moved to S2
Copy the code

Data in the stack is replicated without transferring ownership:

// Initializes a stack string
let s1 = "hello";

// s2 will copy to S1
let s2 = s1;

// get s1 and s2
println!("{}" s1); // "hello"
println!("{}" s2); // "hello"
Copy the code

Copy heap data using data clone:

let s1 = String::from("hello");

// copy s1's data to S2
let s2 = s1.clone();

// get s1 and s2
println!("{}" s1); // "hello"
println!("{}" s2); // "hello"
Copy the code

It makes no difference whether clone is used when there are data types in the stack:

let s1 = "hello";

// copy s1's data to S2
let s2 = s1.clone();
/ / equivalent to the
// let s2 = s1;

// get s1 and s2
println!("{}" s1); // "hello"
println!("{}" s2); // "hello"
Copy the code

In fact, using strings as an example, numbers and chars are also stored on the stack and are replicated, not transferred.

About tuples

If all members of a tuple are stack data, the tuple can also be copied:

// Define a tuple containing the string, char, and number types
let t1 = ("abc".'c'.1);

// copy t1 to T2
let t2 = t1;

// Try to get
println!("{:? }", t1); // ("abc", 'c', 1)
println!("{:? }", t2); // ("abc", 'c', 1)
Copy the code

If one of the members is not replicable, then the tuple is not replicable either, just a simple transfer of ownership:


// Initialize a tuple containing the data in the heap
let t1 = ("abc".'c'.String::new());

// t1 transfers ownership to T2
let t2 = t1;

// Try to get
println!("{:? }", t2);
println!("{:? }", t1); // The ownership of t1 has been moved to T2
Copy the code

Allows members to be moved, but cannot be used again:

let t1 = (String::from("hello"), String::from("world"));

// Transfer ownership of the first member to s
let s = t1.0;

println!("{}", s); // "hello"
println!("{}", t1.1); // "world"
println!("{:? }", t1); // The ownership of the member has been moved
println!("{}", t1.0); // The ownership of the member has been moved
Copy the code

The array can also be copied if its members are in the stack:

let t1 = (1.2);

// copy the first member to t2
let t2 = t1.0;

println!("{}", t2); / / 1
println!("{}", t1.0); / / 1
println!("{}", t1.1); / / 2
Copy the code

The rules for arrays are the same as the rules for tuples, which are not described here and can be practiced on your own

Ownership and function parameters

When a function is called, passing arguments to the function also transfers ownership:

// Since the function argument type is heap data, there is a transfer of ownership
fn take_ownership(s: String) {
  println!("{}", s) // "hello"
}

// Define a heap of data
let s = String::from("hello");

// Calling the function transfers ownership of s to the function
take_ownership(s);

// Try to get s
println!("{}", s); // Error, ownership was taken by take_ownership
Copy the code

The data in the stack is replicated:

// Since the function argument type is stack data, replication occurs
fn makes_copy(n: i32) {
   println!("{}", n) / / 5
}

// Define a stack of data
let num = 5;

// Calling the function copies num
makes_copy(num);

// num is still valid
println!("{}", num); / / 5
Copy the code

Return value and scope

When a variable holding heap data leaves function scope, its data is cleaned up and recycled, but the data can be returned without being destroyed by returning a value:

// Creates a heap data string and returns ownership
fn gives_owner_ship() - >String {
  let s = String::from("hello");
  s
}

// The function takes ownership of a string of data and returns it directly
fn takes_and_gives_back(s: String) - >String {
  s
}

// Gets the ownership of the string created in the function
let s1 = gives_owner_ship();

// Transfer the ownership of S2 to the function, and transfer the ownership to s3 via the return value of the function
let s2 = String::from("world");
let s3 = takes_and_gives_back(s2);

println!("{}", s1); // "hello"
println!("{}", s3); // "world"
println!("{}", s2); // The ownership of s2 has been transferred to S3
Copy the code

Retrieve ownership using the return value:

fn get_lenth(s: String) - > (String.usize) {
  let len = s.len(); // Get the length of s
  (s, len) // Return the ownership and length of s
}

let s1 = String::from("hello");

// Use the return value to regain ownership
let (s1, len) = get_lenth(s1);

println!("{} - {}", s1, len); // "hello-5"
Copy the code

Reference variables

The transfer of ownership of data can reduce the flexibility of development, so there is a concept of reference to solve this problem:

let s1 = String::from("hello");

// Use ampersand to create two references to s1. This does not take ownership of s1
let s2 = &s1;
let s3 = &s2;

/ / to get
println!("{} - {} - {}", s1, s2, s3); // "hello-hello-hello"
Copy the code

The function also supports reference types:

// Specify parameters for the reference type
fn get_length(s: &String) - >usize {
  // where s refers to s1 refers to String real data
  // Since S does not take ownership of the String data, there is no need to return ownership with the return value
  s.len()
}

let s1 = String::from("hello");

// pass a reference to s1 to the function
let len = get_length(&s1);

// s1 is still available
println!("{} - {}", s1, len); // "hello-5"
Copy the code

Use & to reference data, but not to alter it:

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

// try to change the data via s2
s2.push("world"); S2 is an immutable reference and cannot be changed
Copy the code

Make changes to data using mutable references:

// Declare the argument as a mutable reference with &mut
fn get_length(s: &mut String) - >usize {
  s.push_str("world");
  s.len()
}

// Declare mutable data
let mut s1 = String::from("hello");

// Pass in variable reference variables
let len = get_length(&mut s1);

// there is a data change in s1
println!("{} - {}", len, s1); // "10-helloworld"
Copy the code

Only one mutable reference can be declared at a time for data in a single scope:

let mut s1 = String::from("hello");
let s2 = &mut s1;
let s3 = &mut s1 // Error, cannot have multiple mutable references
Copy the code

Mutable references and function arguments to mutable references cannot also exist in a single scope:

let mut s1 = String::from("hello");
let s2 = &mut s1;
let len = get_length(&mut s1); // an error is reported. There can be no more than one mutable reference, because both pass-arguments and s2 are mutable references
Copy the code

Mutable and immutable references cannot exist together:

let mut s1 = String::from("hello");

// Create immutable references
let s2 = &s1; S1 cannot be immutable because there are mutable references below

// Create mutable references
let mut s3 = &mut s1; S1 cannot be mutable because there is an immutable reference on it

// If the following values are used, no error will be reported
println!("{}", s1);
println!("{}", s2);
println!("{}", s3);
Copy the code

Rules of reference

  • If you can have either one mutable reference or any number of immutable references at any given time, think about whether the immutable property of immutable variables still makes sense if the data changes.
  • Rust ensures that references are always valid.