Rust > Ownership
Move
s1
is moved to s2
. s1
is no longer valid:
let s1 = String::from("hello");
let s2 = s1;
println!("{}, world!", s1);
Clone
To solve this problem tt’s possible to clone:
let s1 = String::from("hello");
let s2 = s1.clone();
println!("s1 = {}, s2 = {}", s1, s2);
Ownership and Functions
Moving variable into a functiontion:
let s = String::from("hello"); // s comes into scope
takes_ownership(s); // s's value moves into the function...
// ... and so is no longer valid here
Return Values and Scope
There is one possible solution: function can take anr return ownership:
fn main() {
let s1 = gives_ownership(); // gives_ownership moves its return
// value into s1
let s2 = String::from("hello"); // s2 comes into scope
let s3 = takes_and_gives_back(s2); // s2 is moved into
// takes_and_gives_back, which also
// moves its return value into s3
} // Here, s3 goes out of scope and is dropped. s2 was moved, so nothing
// happens. s1 goes out of scope and is dropped.
fn gives_ownership() -> String { // gives_ownership will move its
// return value into the function
// that calls it
let some_string = String::from("yours"); // some_string comes into scope
some_string // some_string is returned and
// moves out to the calling
// function
}
// This function takes a String and returns one
fn takes_and_gives_back(a_string: String) -> String { // a_string comes into
// scope
a_string // a_string is returned and moves out to the calling function
}
Rust does let us return multiple values using a tuple, as shown here:
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() returns the length of a String
(s, length)
}
References and Borrowing
We can pass reference instand of variable
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()
}
It’s not possible to change value by reference. This code won’t work:
fn main() {
let s = String::from("hello");
change(&s);
}
fn change(some_string: &String) {
some_string.push_str(", world"); // !error
}
Mutable References
Example:
fn main() {
let mut s = String::from("hello");
change(&mut s);
}
fn change(some_string: &mut String) {
some_string.push_str(", world");
}
Important restriction: if you have a mutable reference to a value, you can have no other references to that value.
This work work:
let mut s = String::from("hello");
let r1 = &mut s;
let r2 = &mut s; // error
println!("{}, {}", r1, r2);
Dangling References
This code will cause an error:
fn main() {
let reference_to_nothing = dangle();
}
fn dangle() -> &String { // dangle returns a reference to a String
let s = String::from("hello"); // s is a new String
&s // we return a reference to the String, s
} // Here, s goes out of scope, and is dropped. Its memory goes away.
// Danger!
The Slice Type
A string slice is a reference to part of a String
, and it looks like this:
fn main() {
let s = String::from("hello world");
let hello = &s[0..5];
let world = &s[6..11];
}
This code won’t work:
fn main() {
let mut s = String::from("hello world");
let word = first_word(&s);
s.clear(); // error! we try to change and we have slice
//(which is immutable reference by it's nature)
println!("the first word is: {}", word);
}
String literals
let s = "Hello, world!";
The type of s here is &str: it’s a slice pointing to that specific point of the binary. This is also why string literals are immutable; &str is an immutable reference.