This type is allocated on the heap and as such is able to store an amount of text that is unknown to us at compile time.
#![allow(unused_variables)] fn main() { let s = String::from("hello"); }
#![allow(unused_variables)] fn main() { let mut s = String::from("hello");
s.push_str(", world!"); // push_str() appends a literal to a String
println!("{}", s); // This will print `hello, world!` }
release memory
#![allow(unused_variables)] fn main() { { let s = String::from("hello"); // s is valid from this point forward
// do stuff with s } // this scope is now over, and s is no // longer valid }
There is a natural point at which we can return the memory our String needs to the operating system: when s goes out of scope. When a variable goes out of scope, Rust calls a special function for us. This function is called drop, and it’s where the author of String can put the code to return the memory. Rust calls drop automatically at the closing curly bracket.
#![allow(unused_variables)] fn main() { let s1 = String::from("hello"); let s2 = s1; //Rust considers s1 to no longer be valid }
actually is a move operation
deep copy use clone
#![allow(unused_variables)] fn main() { let s1 = String::from("hello"); let s2 = s1.clone();
println!("s1 = {}, s2 = {}", s1, s2); }
return value and scope
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 goes out of scope but 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("hello"); // some_string comes into scope
some_string // some_string is returned and // moves out to the calling // function }
// takes_and_gives_back will take a String and return 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 }
return multiple value use tuple
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) }
slice
#![allow(unused_variables)] fn main() { let s = String::from("hello world");
let hello = &s[0..5]; let world = &s[6..11]; }
The start..end syntax is a range that begins at start and continues up to, but not including, end. If we wanted to include end, we can use ..= instead of ..:
#![allow(unused_variables)] fn main() { let s = String::from("hello world");