Referencje i pożyczki
Przekazywanie wartości przez referencję
- Referencja do wartości definiowana jest za pomocą operatora
&
, np.let s = String::from("sample"); let s_ref = &s;
- Definicja parametrów w postaci referencji nazywa się pożyczaniem wartości. W takim przypadku funkcja nie posiada pożyczonej wartości i musi ją oddać po zakończeniu działania.
fn main() { let s1 = String::from("sample"); let s2 = process_text(&s1); // lends the s1 value through a reference println!("{} -> {}", s1, s2) // both s1 and s2 are valid } fn process_text(s: &String) -> String { // borrow s value and return new string s.to_uppercase() }
Mutowalne referencje
- Wrócmy do funkcji, która chce przetworzyć podany tekst (tym razem będziemy chcieli skrócić dany tekst), ale spróbujmy zrobić to za pomocą pożyczenia wartości
fn main() { let mut s1 = String::from("sample text"); process_text(&s1, 4); println!("{}", s1); } fn process_text(s: &String, len : usize ) { s.truncate(len) }
- Uruchom program i zastanów się na błędami kompilacji
- Aby poprawić kod, nie tylko zmienna musi być mutowalna, ale również musimy jawnie wypożyczyć wartość w sposób, który umożliwi modyfikację wartości
fn main() { let mut s1 = String::from("sample text"); process_text(&mut s1, 4); println!("{}", s1); } fn process_text(s: &mut String, len : usize) { s.truncate(len) }
Praca z referencjami
Reguły dotyczące referencji
W dowolnym momencie programu, dla jednej wartości możemy mieć tylko jeden z poniższych przypadków:
- jedną mutowalną referencję,
- dowolną liczbę niemutowalnych referencji.
Ponadto wszystkie odwołania muszą być ważne, ie. muszą odnosić się do wartości, które nie są poza zakresem.
Uruchom kolejne fragmenty kodu i wyjaśnij, której reguły nie spełnia
let mut s = String::from("sample");
let s1 = &mut s;
let s2 = &mut s;
println!("{}, {}", s1, s2);
let mut s = String::from("sample");
let s1 = &mut s;
let s2 = &s;
println!("{}, {}", s1, s2);
let mut s = String::from("sample");
let s1 = &mut s;
let s2 = s;
println!("{}, {}", s1, s2);
fn main() {
let s = generate_ref_to_string();
}
fn generate_ref_to_string() -> &String {
&String::from("dangling text")
}
Ćwiczenie - Popraw kod
- Popraw poniższy kod tak, aby się skompilował, możesz tylko zmienić kolejność linii w funkcji
main
. Wyjaśnij dlaczego kod po zmianach jest poprawny.fn main() { let mut x = 100; let y = &mut x; let z = &mut x; *y += 100; *z += 1000; assert_eq!(x, 1200); }
- Popraw kod jedynie poprzez dodawanie lub usuwanie referencji
fn main() { let data = "Rust is great!".to_string(); get_char(data); string_uppercase(&data); } // Should not take ownership fn get_char(data: String) -> char { data.chars().last().unwrap() } // Should take ownership fn string_uppercase(mut data: &String) { data = &data.to_uppercase(); println!("{}", data); }
Źródło: powyższe zadania pochodzą z oficjalnych ćwiczeń rustlings, dostępnych w repozytorium https://github.com/rust-lang/rustlings/. Znajdziesz tam dużo więcej podobnych zadań.