Присвояване, копиране, референции
10 октомври 2024
Административни неща
- Следете за новини на сайта: https://fmi.rust-lang.bg
Административни неща
- Следете за новини на сайта: https://fmi.rust-lang.bg
- Инсталирайте си Rust: https://rustup.rs/
Административни неща
- Следете за новини на сайта: https://fmi.rust-lang.bg
- Инсталирайте си Rust: https://rustup.rs/
- Елате в Discord канала: https://discord.gg/FCTNfbZ
Преговор
- Инсталация и подкарване (
rustup
,cargo
, Rust Playground)
Преговор
- Инсталация и подкарване (
rustup
,cargo
, Rust Playground) - Документация (The Rust Book,
rustup doc
)
Преговор
- Инсталация и подкарване (
rustup
,cargo
, Rust Playground) - Документация (The Rust Book,
rustup doc
) - Числа (конвертиране)
Преговор
- Инсталация и подкарване (
rustup
,cargo
, Rust Playground) - Документация (The Rust Book,
rustup doc
) - Числа (конвертиране)
- Променливи (отгатване на типове, скриване на променливи, мутация)
Преговор
- Инсталация и подкарване (
rustup
,cargo
, Rust Playground) - Документация (The Rust Book,
rustup doc
) - Числа (конвертиране)
- Променливи (отгатване на типове, скриване на променливи, мутация)
- Control flow (
if
,while
,loop
)
Преговор
- Инсталация и подкарване (
rustup
,cargo
, Rust Playground) - Документация (The Rust Book,
rustup doc
) - Числа (конвертиране)
- Променливи (отгатване на типове, скриване на променливи, мутация)
- Control flow (
if
,while
,loop
) - Твърдения и изрази (точка-и-запетаи!)
Преговор
- Инсталация и подкарване (
rustup
,cargo
, Rust Playground) - Документация (The Rust Book,
rustup doc
) - Числа (конвертиране)
- Променливи (отгатване на типове, скриване на променливи, мутация)
- Control flow (
if
,while
,loop
) - Твърдения и изрази (точка-и-запетаи!)
- Функции (
fn
, връщане на стойности)
Преговор
- Инсталация и подкарване (
rustup
,cargo
, Rust Playground) - Документация (The Rust Book,
rustup doc
) - Числа (конвертиране)
- Променливи (отгатване на типове, скриване на променливи, мутация)
- Control flow (
if
,while
,loop
) - Твърдения и изрази (точка-и-запетаи!)
- Функции (
fn
, връщане на стойности) - Печатане на стандартния изход (
println!("x = {}", x)
)
Низове
Низове литерали
let a = "Hello";
let b = a;
println!("{}!", a);
println!("{} again!", b);
Hello! Hello again!
fn main() { let a = "Hello"; let b = a; println!("{}!", a); println!("{} again!", b); }
Низове
Низове литерали
let a: &'static str = "Hello";
fn main() { let a: &'static str = "Hello"; }
Низове
Низове литерали
let a: &'static str = "Hello";
fn main() { let a: &'static str = "Hello"; }
- "статична" референция към
str
Низове
Низове литерали
let a: &'static str = "Hello";
fn main() { let a: &'static str = "Hello"; }
- "статична" референция към
str
- константна данна, не може да се променя
Низове
Низове литерали
let a = "Hello";
let b = " again!";
let message = a + b;
error[E0369]: cannot add `&str` to `&str` --> src/bin/main_ff85fd0fe5cb754d5812d4e753254866d53e2a47.rs:4:17 | 4 | let message = a + b; | - ^ - &str | | | | | `+` cannot be used to concatenate two `&str` strings | &str | = note: string concatenation requires an owned `String` on the left help: create an owned `String` from a string reference | 4 | let message = a.to_owned() + b; | +++++++++++ For more information about this error, try `rustc --explain E0369`. error: could not compile `rust` (bin "main_ff85fd0fe5cb754d5812d4e753254866d53e2a47") due to 1 previous error
fn main() { let a = "Hello"; let b = " again!"; let message = a + b; }
Низове
Динамични низове
let mut a = String::from("Hello");
println!("{}!", a);
a.push_str(" again!");
println!("{}", a);
Hello! Hello again!
fn main() { let mut a = String::from("Hello"); println!("{}!", a); a.push_str(" again!"); println!("{}", a); }
Низове
Динамични низове
let a: String = String::from("Hello");
fn main() { let a: String = String::from("Hello"); }
Низове
Динамични низове
let a: String = String::from("Hello");
fn main() { let a: String = String::from("Hello"); }
- динамично заделя памет за низа
Низове
Динамични низове
let a: String = String::from("Hello");
fn main() { let a: String = String::from("Hello"); }
- динамично заделя памет за низа
- стойността може да се променя
Низове
Динамични низове
Следните начини за създаване на динамичен низ са напълно еквивалентни
let s1 = String::from("Hello");
let s2 = "Hello".to_string();
let s3 = "Hello".to_owned();
fn main() { let s1 = String::from("Hello"); let s2 = "Hello".to_string(); let s3 = "Hello".to_owned(); }
Присвояване
Присвояване
Статични низове
let a = "Hello";
let b = a;
println!("{}!", a);
println!("{} again!", b);
fn main() { let a = "Hello"; let b = a; println!("{}!", a); println!("{} again!", b); }
Присвояване
Статични низове
let a = "Hello";
let b = a;
println!("{}!", a);
println!("{} again!", b);
Hello! Hello again!
fn main() { let a = "Hello"; let b = a; println!("{}!", a); println!("{} again!", b); }
Присвояване
Динамични низове
let s1 = String::from("Cookies!");
let s2 = s1;
println!("{}", s1);
println!("Mmm, {}", s2);
Присвояване
Динамични низове
let s1 = String::from("Cookies!");
let s2 = s1;
println!("{}", s1);
println!("Mmm, {}", s2);
error[E0382]: borrow of moved value: `s1` --> src/bin/main_9bd3356d7798f6992589bc25a22e3d30f92002bb.rs:5:16 | 2 | let s1 = String::from("Cookies!"); | -- move occurs because `s1` has type `String`, which does not implement the `Copy` trait 3 | let s2 = s1; | -- value moved here 4 | 5 | println!("{}", s1); | ^^ value borrowed here after move | = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider cloning the value if the performance cost is acceptable | 3 | let s2 = s1.clone(); | ++++++++ For more information about this error, try `rustc --explain E0382`. error: could not compile `rust` (bin "main_9bd3356d7798f6992589bc25a22e3d30f92002bb") due to 1 previous error
fn main() { let s1 = String::from("Cookies!"); let s2 = s1; println!("{}", s1); println!("Mmm, {}", s2); }
Присвояване
Ownership
Собственост
Собственост
В някои други езици (с менaжирана памет)
Foo myFoo = new Foo();
myObj1.foo = myFoo;
myObj2.foo = myFoo;
- няма концепция за собственост над стойност или обект
- всички места, от където се реферира обект, държат указател към динамично заделен обект
- всички места, от където се реферира обект, са равноправни
Собственост
В Rust (и други езици)
let my_foo = Foo::new();
{
let foo_ref = &my_foo;
// do work with `foo_ref`
}
struct Foo {} impl Foo { fn new() -> Self { Foo{} } } fn main() { let my_foo = Foo::new(); { let foo_ref = &my_foo; // do work with `foo_ref` } }
- променливата
my_foo
има собстеност над стойността - променливата
my_foo
оказва къде в паметта ще бъде записана стойността - стойността може да има само един собственик
- могат да се вземат временни референции към стойността
Собственост
В Rust (и други езици)
let my_foo = Foo::new();
{
let foo_ref = &my_foo;
// do work with `foo_ref`
}
struct Foo {} impl Foo { fn new() -> Self { Foo{} } } fn main() { let my_foo = Foo::new(); { let foo_ref = &my_foo; // do work with `foo_ref` } }
- променливата
my_foo
има собстеност над стойността - променливата
my_foo
оказва къде в паметта ще бъде записана стойността - стойността може да има само един собственик
- могат да се вземат временни референции към стойността
- (споделена собственост се имплементира с библиотечен тип)
Собственост
Lifetime
fn some_func() {
let foo1 = Foo::new();
let foo2 = Foo::new();
} // извиква се деструктора на `foo2`,
// после и на `foo1`
struct Foo {} impl Foo { fn new() -> Self { Foo{} } } fn main() {} fn some_func() { let foo1 = Foo::new(); let foo2 = Foo::new(); } // извиква се деструктора на `foo2`, // после и на `foo1`
- когато собственикът излезе от scope, се извиква деструктура на стойността
Семантика на местене (Move semantics)
При присвояване се прехвърля собствеността над стойността
let s1 = String::from("Cookies!");
// `s1` е собственик на стойността "низа Cookies!"
let s2 = s1;
// `s2` е собственик на стойността "низа Cookies!"
println!("Mmm, {}", s2);
Mmm, Cookies!
fn main() { let s1 = String::from("Cookies!"); // `s1` е собственик на стойността "низа Cookies!" let s2 = s1; // `s2` е собственик на стойността "низа Cookies!" println!("Mmm, {}", s2); }
Семантика на местене (Move semantics)
При присвояване се прехвърля собствеността над стойността
let s1 = String::from("Cookies!");
// `s1` е собственик на стойността "низа Cookies!"
let s2 = s1;
// `s2` е собственик на стойността "низа Cookies!"
println!("Mmm, {}", s2);
Mmm, Cookies!
fn main() { let s1 = String::from("Cookies!"); // `s1` е собственик на стойността "низа Cookies!" let s2 = s1; // `s2` е собственик на стойността "низа Cookies!" println!("Mmm, {}", s2); }
- променливата
s2
става новия собственик, променливатаs1
спира да е собственик
Семантика на местене (Move semantics)
При присвояване се прехвърля собствеността над стойността
let s1 = String::from("Cookies!");
// `s1` е собственик на стойността "низа Cookies!"
let s2 = s1;
// `s2` е собственик на стойността "низа Cookies!"
println!("Mmm, {}", s2);
Mmm, Cookies!
fn main() { let s1 = String::from("Cookies!"); // `s1` е собственик на стойността "низа Cookies!" let s2 = s1; // `s2` е собственик на стойността "низа Cookies!" println!("Mmm, {}", s2); }
- променливата
s2
става новия собственик, променливатаs1
спира да е собственик - стойността не се копира
Семантика на местене (Move semantics)
При присвояване се прехвърля собствеността над стойността
let s1 = String::from("Cookies!");
// `s1` е собственик на стойността "низа Cookies!"
let s2 = s1;
// `s2` е собственик на стойността "низа Cookies!"
println!("Mmm, {}", s2);
Mmm, Cookies!
fn main() { let s1 = String::from("Cookies!"); // `s1` е собственик на стойността "низа Cookies!" let s2 = s1; // `s2` е собственик на стойността "низа Cookies!" println!("Mmm, {}", s2); }
- променливата
s2
става новия собственик, променливатаs1
спира да е собственик - стойността не се копира
- стойността не се променя
Семантика на местене (Move semantics)
След преместване старата променлива не може да се използва.
let s1 = String::from("Cookies!");
let s2 = s1;
println!("{}", s1);
println!("Mmm, {}", s2);
error[E0382]: borrow of moved value: `s1` --> src/bin/main_9bd3356d7798f6992589bc25a22e3d30f92002bb.rs:5:16 | 2 | let s1 = String::from("Cookies!"); | -- move occurs because `s1` has type `String`, which does not implement the `Copy` trait 3 | let s2 = s1; | -- value moved here 4 | 5 | println!("{}", s1); | ^^ value borrowed here after move | = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider cloning the value if the performance cost is acceptable | 3 | let s2 = s1.clone(); | ++++++++ For more information about this error, try `rustc --explain E0382`. error: could not compile `rust` (bin "main_9bd3356d7798f6992589bc25a22e3d30f92002bb") due to 1 previous error
fn main() { let s1 = String::from("Cookies!"); let s2 = s1; println!("{}", s1); println!("Mmm, {}", s2); }
Семантика на местене (Move semantics)
Ако искаме да ползваме и двете променливи, трябва да направим копие на стойността
let s1 = String::from("Cookies!");
let s2 = s1.clone();
println!("{}", s1);
println!("Mmm, {}", s2);
Cookies! Mmm, Cookies!
fn main() { let s1 = String::from("Cookies!"); let s2 = s1.clone(); println!("{}", s1); println!("Mmm, {}", s2); }
Семантика на местене (Move semantics)
При преместване:
- паметта на старата променлива се копира побитово в новата променлива
- старата променлива се маркира като невалидна за компилатора
Примери
Функции с подаване на String
При подаването на аргументи към функция важат същите семантики
fn main() {
let s = String::from("hello"); // Дефинираме `s`
takes_ownership(s); // Стойността на `s` се мести във функцията и
// затова не е валидна след този ред.
} // Тук `s` излиза от scope, но `s` е преместен и съответно не се деалокара.
fn takes_ownership(some_string: String) {
println!("{}", some_string);
} // `some_string` излиза от scope и се освобождава паметта.
hello
fn main() { let s = String::from("hello"); // Дефинираме `s` takes_ownership(s); // Стойността на `s` се мести във функцията и // затова не е валидна след този ред. } // Тук `s` излиза от scope, но `s` е преместен и съответно не се деалокара. fn takes_ownership(some_string: String) { println!("{}", some_string); } // `some_string` излиза от scope и се освобождава паметта.
Примери
Функции които връщат стойност
Връщането на стойност от функция също може да прехвърля собственост
fn main() {
let s1 = gives_ownership();
let s2 = takes_and_gives_back(s1);
}
fn gives_ownership() -> String {
let some_string = String::from("hello");
some_string // Преместваме стойността към функцията, която ни е извикала
}
fn takes_and_gives_back(a_string: String) -> String {
a_string
}
fn main() { let s1 = gives_ownership(); let s2 = takes_and_gives_back(s1); } fn gives_ownership() -> String { let some_string = String::from("hello"); some_string // Преместваме стойността към функцията, която ни е извикала } fn takes_and_gives_back(a_string: String) -> String { a_string }
Примери
Функции, които не прехвърлят собственост
А как може да продължим да използваме стойността след извикването на функцията?
(Очевидно това не е удобен вариант)
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();
(s, length)
}
The length of 'hello' is 5.
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(); (s, length) }
Референции
Референции
- позволяват заемане на стойност за определен период
Референции
позволяват заемане на стойност за определен период
споделена референция
- immutable borrow
- тип
&T
- взимане на референция:
let ref_x = &x;
- дереференциране:
*ref_x;
Референции
позволяват заемане на стойност за определен период
споделена референция
- immutable borrow
- тип
&T
- взимане на референция:
let ref_x = &x;
- дереференциране:
*ref_x;
ексклузивна референция
- mutable borrow
- тип
&mut T
- взимане на референция
let ref_mut_x = &mut x;
- дереференциране:
*ref_mut_x;
Референции
- референцията е указател - адрес в паметта
Референции
- референцията е указател - адрес в паметта
- който не е null
Референции
- референцията е указател - адрес в паметта
- който не е null
- който винаги сочи към валидна стойност
- алокирана и инициализирана
- жива - не е преместена или деалокиана след вземането на референцията
- това се проверява по време на компилация
Примери
Заемане на стойност (borrowing)
fn main() {
let x = 123;
{
let ref_x = &x;
println!("x = {}", ref_x);
}
println!("x = {}", x);
}
x = 123 x = 123
fn main() { let x = 123; { let ref_x = &x; println!("x = {}", ref_x); } println!("x = {}", x); }
Примери
Валидност
fn main() {
let r;
{
let s = String::from("hello");
r = &s;
}
println!("{}", r);
}
error[E0597]: `s` does not live long enough --> src/bin/main_71adf588d2710ea65d8d814495506ae9ee825001.rs:6:13 | 5 | let s = String::from("hello"); | - binding `s` declared here 6 | r = &s; | ^^ borrowed value does not live long enough 7 | } | - `s` dropped here while still borrowed 8 | 9 | println!("{}", r); | - borrow later used here For more information about this error, try `rustc --explain E0597`. error: could not compile `rust` (bin "main_71adf588d2710ea65d8d814495506ae9ee825001") due to 1 previous error
fn main() { let r; { let s = String::from("hello"); r = &s; } println!("{}", r); }
Примери
Валидност
fn returns_string() -> &String {
let s = String::from("hello");
&s
}
error[E0106]: missing lifetime specifier --> src/bin/main_5cb43ac4011a50cf250ff0feb51cfdfd0cadb93a.rs:2:24 | 2 | fn returns_string() -> &String { | ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static` | 2 | fn returns_string() -> &'static String { | +++++++ help: instead, you are more likely to want to return an owned value | 2 - fn returns_string() -> &String { 2 + fn returns_string() -> String { | error[E0515]: cannot return reference to local variable `s` --> src/bin/main_5cb43ac4011a50cf250ff0feb51cfdfd0cadb93a.rs:4:5 | 4 | &s | ^^ returns a reference to data owned by the current function Some errors have detailed explanations: E0106, E0515. For more information about an error, try `rustc --explain E0106`. error: could not compile `rust` (bin "main_5cb43ac4011a50cf250ff0feb51cfdfd0cadb93a") due to 2 previous errors
fn main() {} fn returns_string() -> &String { let s = String::from("hello"); &s }
Примери
Подаване на функция
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()
}
The length of 'hello' is 5.
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() }
Примери
Мутиране през функция
fn main() {
let mut s = String::from("hello");
change(&mut s);
println!("{}", s);
}
fn change(some_string: &mut String) {
some_string.push_str(", world");
}
hello, world
fn main() { let mut s = String::from("hello"); change(&mut s); println!("{}", s); } fn change(some_string: &mut String) { some_string.push_str(", world"); }
Референции и преместване
Не може да преместим стойност, докато съществува референция към нея
fn main() {
let s1 = String::from("hello");
let r = &s1;
let s2 = s1;
println!("{}", r);
}
error[E0505]: cannot move out of `s1` because it is borrowed --> src/bin/main_c6f2dc9cfd0028307ee888b509415f2aa84fc40e.rs:5:14 | 2 | let s1 = String::from("hello"); | -- binding `s1` declared here 3 | let r = &s1; | --- borrow of `s1` occurs here 4 | 5 | let s2 = s1; | ^^ move out of `s1` occurs here 6 | println!("{}", r); | - borrow later used here | help: consider cloning the value if the performance cost is acceptable | 3 - let r = &s1; 3 + let r = s1.clone(); | For more information about this error, try `rustc --explain E0505`. error: could not compile `rust` (bin "main_c6f2dc9cfd0028307ee888b509415f2aa84fc40e") due to 1 previous error
fn main() { let s1 = String::from("hello"); let r = &s1; let s2 = s1; println!("{}", r); }
Пример
Референция към скрита (shadowed) променлива
let s = String::from("first");
let r = &s;
let s = String::from("second");
println!("{}", r);
println!("{}", s);
first second
fn main() { let s = String::from("first"); let r = &s; let s = String::from("second"); println!("{}", r); println!("{}", s); }
Пример
Референция към скрита (shadowed) променлива
let s = String::from("hello");
let s = &s;
println!("{}", s);
hello
fn main() { let s = String::from("hello"); let s = &s; println!("{}", s); }
Пример
Референция към временна стойност
let s = &String::from("hello");
println!("{}", s);
hello
fn main() { let s = &String::from("hello"); println!("{}", s); }
Пример
Референция към временна стойност
let s = &mut String::from("hello");
s.push_str(", world");
println!("{}", s);
hello, world
fn main() { let s = &mut String::from("hello"); s.push_str(", world"); println!("{}", s); }
Borrow checker
Borrow checker
Правило
- По всяко време към една стойност може да съществува най-много едно от следните:
Borrow checker
Правило
- По всяко време към една стойност може да съществува най-много едно от следните:
- точно една екслкузивна референция (
&mut T
)
- точно една екслкузивна референция (
Borrow checker
Правило
- По всяко време към една стойност може да съществува най-много едно от следните:
- точно една екслкузивна референция (
&mut T
) - произволен брой споделени референции (
&T
)
- точно една екслкузивна референция (
Borrow checker
Защо?
- голяма категория проблеми са породени от "shared mutable state"
Borrow checker
Защо?
- голяма категория проблеми са породени от "shared mutable state"
- data races при многонишкови програми
Borrow checker
Защо?
- голяма категория проблеми са породени от "shared mutable state"
- data races при многонишкови програми
- други видове бъгове при еднонишкови
Borrow checker
Защо?
- голяма категория проблеми са породени от "shared mutable state"
- data races при многонишкови програми
- други видове бъгове при еднонишкови
- невъзможност за локален анализ
Borrow checker
Защо?
голяма категория проблеми са породени от "shared mutable state"
- data races при многонишкови програми
- други видове бъгове при еднонишкови
- невъзможност за локален анализ
някои езици (основно чисто функционалните) решават този проблем, като забраняват мутацията
- но това води до други неудобства
Borrow checker
Защо?
голяма категория проблеми са породени от "shared mutable state"
- data races при многонишкови програми
- други видове бъгове при еднонишкови
- невъзможност за локален анализ
някои езици (основно чисто функционалните) решават този проблем, като забраняват мутацията
- но това води до други неудобства
Rust решава проблема, като забранява едновременното споделяне и мутация
- споделяне без мутация през
&T
- мутация без споделяне през
&mut T
- споделяне без мутация през
Borrow checker
let mut s = String::from("hello");
let r1 = &mut s;
let r2 = &mut s;
println!("{}, {}", r1, r2);
Borrow checker
let mut s = String::from("hello");
let r1 = &mut s;
let r2 = &mut s;
println!("{}, {}", r1, r2);
error[E0499]: cannot borrow `s` as mutable more than once at a time --> src/bin/main_5bdec4f559fb3cb8b422d5856bd844f38ad67a0f.rs:5:10 | 4 | let r1 = &mut s; | ------ first mutable borrow occurs here 5 | let r2 = &mut s; | ^^^^^^ second mutable borrow occurs here 6 | 7 | println!("{}, {}", r1, r2); | -- first borrow later used here For more information about this error, try `rustc --explain E0499`. error: could not compile `rust` (bin "main_5bdec4f559fb3cb8b422d5856bd844f38ad67a0f") due to 1 previous error
fn main() { let mut s = String::from("hello"); let r1 = &mut s; let r2 = &mut s; println!("{}, {}", r1, r2); }
Borrow checker
Решение: не ги използвайте заедно
let mut s = String::from("hello");
let r1 = &mut s;
println!("{}", r1);
let r2 = &mut s;
println!("{}", r2);
hello hello
fn main() { let mut s = String::from("hello"); let r1 = &mut s; println!("{}", r1); let r2 = &mut s; println!("{}", r2); }
Borrow checker
За повече яснота за живота на r1
:
let mut s = String::from("hello");
{
let r1 = &mut s;
println!("{}", r1);
}
let r2 = &mut s;
println!("{}", r2);
hello hello
fn main() { let mut s = String::from("hello"); { let r1 = &mut s; println!("{}", r1); } let r2 = &mut s; println!("{}", r2); }
Borrow checker
За повече яснота за живота на r1
:
let mut s = String::from("hello");
{
let r1 = &mut s;
println!("{}", r1);
}
let r2 = &mut s;
println!("{}", r2);
hello hello
fn main() { let mut s = String::from("hello"); { let r1 = &mut s; println!("{}", r1); } let r2 = &mut s; println!("{}", r2); }
- референцията е жива от мемента на създаването и до момента на последното използване
- не е задължена да живее до края на scope-а (non-lexical lifetimes)
Borrow checker
Класически пример от c++ - инвалидация на итератори
let mut vec = vec![1, 2, 3];
for val in vec.iter() {
do_something(val);
if some_condition {
vec.push(99);
}
}
error[E0502]: cannot borrow `vec` as mutable because it is also borrowed as immutable --> src/bin/main_9036c0a5609ad39a3ebd2868f8e68cf6133e5a72.rs:10:9 | 6 | for val in vec.iter() { | ---------- | | | immutable borrow occurs here | immutable borrow later used here ... 10 | vec.push(99); | ^^^^^^^^^^^^ mutable borrow occurs here For more information about this error, try `rustc --explain E0502`. error: could not compile `rust` (bin "main_9036c0a5609ad39a3ebd2868f8e68cf6133e5a72") due to 1 previous error
fn main() { fn do_something(_: &i32) {} let some_condition = false; let mut vec = vec![1, 2, 3]; for val in vec.iter() { do_something(val); if some_condition { vec.push(99); } } }
Низове и резени
Низове и резени
Можем да вземем част от низ
let s = String::from("hello, world");
let r1 = &s[1..4];
println!("{}", r1);
let r2 = &s[..s.len()-2]; // &s[..-2] не е възможно
println!("{}", r2);
let r3 = &s[..];
println!("{}", r3);
ell hello, wor hello, world
fn main() { let s = String::from("hello, world"); let r1 = &s[1..4]; println!("{}", r1); let r2 = &s[..s.len()-2]; // &s[..-2] не е възможно println!("{}", r2); let r3 = &s[..]; println!("{}", r3); }
Низове и резени
Интервали
- какво е
1..4
?
Низове и резени
Интервали
- началото и краят могат да се изпуснат
start..end
start..
..end
..
Низове и резени
Интервали
- началото и краят могат да се изпуснат
start..end
start..
..end
..
- има и затворени интервали
start..=end
..=end
Низове и резени
Интервали
- а какво е
&s[1..4]
?
Низове и резени
Интервали
- а какво е
&s[1..4]
? &str
Низове и резени
Интервали
- а какво е
&s[1..4]
? &str
- резен от низ (string slice)
Низове и резени
Интервали
- а какво е
&s[1..4]
? &str
- резен от низ (string slice)
- специален вид референция
Низове и резени
Интервали
- а какво е
&s[1..4]
? &str
- резен от низ (string slice)
- специален вид референция
- репрезентация -
(ptr, len)
Разполагане в паметта
String
let my_name = String::from("Pascal Precht");
fn main() { let my_name = String::from("Pascal Precht"); }
[––––– my_name: String ––––] +––––––––+––––––––+––––––––+ stack frame │ ptr=• │ cap=16 │ len=13 │ +–––––│––+––––––––+––––––––+ +–––+ │ │ [–│–––––––––––––––––– ptr + len ––––––––––––––––––––] [-│------------------ ptr + cap --------------------------------] +–V–+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+ heap │ P │ a │ s │ c │ a │ l │ ␣ │ P │ r │ e │ c │ h │ t │ │ │ │ +–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+
Разполагане в паметта
&String
let my_name = String::from("Pascal Precht");
let r = &my_name;
fn main() { let my_name = String::from("Pascal Precht"); let r = &my_name; }
+–––––––––––––––––––––––––––––––––––+ │ │ [–│––– my_name: String ––––] [ r: &String ] +–V––––––+––––––––+––––––––+ +–––––––│––––+ stack frame │ ptr=• │ cap=16 │ len=13 │ │ ptr=• │ +–––––│––+––––––––+––––––––+ +––––––––––––+ +–––+ │ │ [–│–––––––––––––––––– ptr + len ––––––––––––––––––––] [-│------------------ ptr + cap --------------------------------] +–V–+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+ heap │ P │ a │ s │ c │ a │ l │ ␣ │ P │ r │ e │ c │ h │ t │ │ │ │ +–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+
Разполагане в паметта
&str
let my_name = String::from("Pascal Precht");
let last_name = &my_name[7..];
fn main() { let my_name = String::from("Pascal Precht"); let last_name = &my_name[7..]; }
[––––– my_name: String ––––] [last_name: &str] +––––––––+––––––––+––––––––+ +–––––––+–––––––+ stack frame │ ptr=• │ cap=16 │ len=13 │ │ ptr=• │ len=6 │ +–––––│––+––––––––+––––––––+ +–––––│–+–––––––+ +–––+ │ │ +–––––+ │ │ │ │ │ [–│––––––– str –––––––––] +–V–+–––+–––+–––+–––+–––+–––+–V–+–––+–––+–––+–––+–––+–––+–––+–––+ heap │ P │ a │ s │ c │ a │ l │ ␣ │ P │ r │ e │ c │ h │ t │ │ │ │ +–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+
Разполагане в паметта
&str
let my_name = "Pascal Precht";
let last_name = &my_name[7..];
fn main() { let my_name = "Pascal Precht"; let last_name = &my_name[7..]; }
[– my_name: &str –] [last_name: &str] +––––––––+––––––––+ +–––––––+–––––––+ stack frame │ ptr=• │ len=13 │ │ ptr=• │ len=6 │ +–––––│––+––––––––+ +–––––│–+–––––––+ +–––+ │ │ +–––––+ │ │ │ │ │ [–│––––––– str –––––––––] +–V–+–––+–––+–––+–––+–––+–––+–V–+–––+–––+–––+–––+–––+ .rodata │ P │ a │ s │ c │ a │ l │ ␣ │ P │ r │ e │ c │ h │ t │ +–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+
Масиви и резени
Масиви и резени
Типа [T;N]
- хомогенен масив с фиксиран брой елементи
- размера трябва да се знае по време на компилация
let point_coords = [1.0, 3.5, 0.0]; // тип [f32; 3]
println!("{:?}", point_coords);
[1.0, 3.5, 0.0]
fn main() { let point_coords = [1.0, 3.5, 0.0]; // тип [f32; 3] println!("{:?}", point_coords); }
Масиви и резени
Типа [T;N]
- синтаксис за попълване с фиксирана стойност
[<елемент>; <брой>]
let zero = [0.0; 3]; // тип [f32; 3]
println!("{:?}", zero);
[0.0, 0.0, 0.0]
fn main() { let zero = [0.0; 3]; // тип [f32; 3] println!("{:?}", zero); }
Вектори
Tипа Vec<T>
- динамичен масив с елементи от тип
T
- автоматично си разширява капацитета при добавяне на елементи
let mut v = Vec::new();
v.push(1);
v.push(2);
v.push(3);
println!("{:?}", v);
[1, 2, 3]
fn main() { let mut v = Vec::new(); v.push(1); v.push(2); v.push(3); println!("{:?}", v); }
Вектори
Типа Vec<T>
- инициализация с макрото
vec!
let v = vec![1, 2, 3];
println!("{:?}", v);
[1, 2, 3]
fn main() { let v = vec![1, 2, 3]; println!("{:?}", v); }
Вектори
Типа Vec<T>
- инициализация с макрото
vec!
let v = vec![0; 8];
println!("{:?}", v);
[0, 0, 0, 0, 0, 0, 0, 0]
fn main() { let v = vec![0; 8]; println!("{:?}", v); }
Резени
let arr = [2, 4, 6, 8, 10];
let arr_slice = &arr[1..4]; // тип &[i32]
println!("{:?}", arr_slice);
let v = vec![2, 4, 6, 8, 10];
let vec_slice = &v[1..4]; // тип &[i32]
println!("{:?}", vec_slice);
[4, 6, 8] [4, 6, 8]
fn main() { let arr = [2, 4, 6, 8, 10]; let arr_slice = &arr[1..4]; // тип &[i32] println!("{:?}", arr_slice); let v = vec![2, 4, 6, 8, 10]; let vec_slice = &v[1..4]; // тип &[i32] println!("{:?}", vec_slice); }
Резени
Типа &[T]
Резени
Типа &[T]
- резен от масив (slice)
Резени
Типа &[T]
- резен от масив (slice)
- репрезентиран като
(ptr, len)
Резени
Типа &[T]
- резен от масив (slice)
- репрезентиран като
(ptr, len)
Резени
Литерали
let slice = &[2, 4, 6, 8, 10];
println!("{:?}", slice);
[2, 4, 6, 8, 10]
fn main() { let slice = &[2, 4, 6, 8, 10]; println!("{:?}", slice); }
Резени
Мутация
let mut v = vec![2, 4, 6, 8, 10, 12];
let slice = &mut v[1..4]; // тип &mut [i32]
for elem in slice.iter_mut() {
*elem += 1;
}
println!("{:?}", slice);
println!("{:?}", v);
[5, 7, 9] [2, 5, 7, 9, 10, 12]
fn main() { let mut v = vec![2, 4, 6, 8, 10, 12]; let slice = &mut v[1..4]; // тип &mut [i32] for elem in slice.iter_mut() { *elem += 1; } println!("{:?}", slice); println!("{:?}", v); }
Резени
Типа &mut [T]
Резени
Типа &mut [T]
- mutable slice
Резени
Типа &mut [T]
- mutable slice
- позволява променяне на отделен елемент (позволява взимане на
&mut T
)
Резени
Типа &mut [T]
- mutable slice
- позволява променяне на отделен елемент (позволява взимане на
&mut T
) - не позволява добавяне или премахване на елементи
Резени
Типа &mut [T]
- mutable slice
- позволява променяне на отделен елемент (позволява взимане на
&mut T
) - не позволява добавяне или премахване на елементи
- (аналогично има и тип
&mut str
)
Обобщение
Масив | Низ | ||
---|---|---|---|
статичен | [T; N] |
- | собственост над стойността |
динамичен | Vec<T> |
String |
собственост над стойността |
резен | &[T] |
&str |
заета назаем стойност (borrow) |
mutable резен | &mut [T] |
&mut str |
заета назаем стойност (borrow) |
Преместване и копиране
Защо преместването е проблем при String
, а не при литералите?
let s1 = "Cookies!";
let s2 = s1;
println!("{}", s1);
println!("Mmm, {}", s2);
Cookies! Mmm, Cookies!
fn main() { let s1 = "Cookies!"; let s2 = s1; println!("{}", s1); println!("Mmm, {}", s2); }
let s1 = String::from("Cookies!");
let s2 = s1;
// println!("{}", s1); // boom
println!("Mmm, {}", s2);
Mmm, Cookies!
fn main() { let s1 = String::from("Cookies!"); let s2 = s1; // println!("{}", s1); // boom println!("Mmm, {}", s2); }
Защо преместването е проблем при String
, а не при литералите?
let s1 = "Cookies!";
let s2 = s1;
println!("{}", s1);
println!("Mmm, {}", s2);
Cookies! Mmm, Cookies!
fn main() { let s1 = "Cookies!"; let s2 = s1; println!("{}", s1); println!("Mmm, {}", s2); }
let s1 = String::from("Cookies!");
let s2 = s1;
// println!("{}", s1); // boom
println!("Mmm, {}", s2);
Mmm, Cookies!
fn main() { let s1 = String::from("Cookies!"); let s2 = s1; // println!("{}", s1); // boom println!("Mmm, {}", s2); }
- защото
String
имплементира самоClone
- а
&str
имплементираClone
иCopy
Клониране
trait Clone
- позволява създаване на копие на обекта
- типовете, които го имплементират, имат метод
clone
Клониране
trait Clone
- позволява създаване на копие на обекта
- типовете, които го имплементират, имат метод
clone
// сигнатурата за `String`
fn clone(s: &String) -> String;
Клониране
trait Clone
- позволява създаване на копие на обекта
- типовете, които го имплементират, имат метод
clone
// сигнатурата за `String`
fn clone(s: &String) -> String;
- ще говорим повече за traits в бъдеща лекция
Клониране
Вече го видяхме в израза s1.clone()
let s1 = String::from("Cookies!");
let s2 = s1.clone();
println!("{}", s1);
println!("Mmm, {}", s2);
Cookies! Mmm, Cookies!
fn main() { let s1 = String::from("Cookies!"); let s2 = s1.clone(); println!("{}", s1); println!("Mmm, {}", s2); }
Копиране
trait Copy
- trait маркер
- указва, че за типа, който го имплементира:
- създаването на копие е еквиваленто на побитово копиране на паметта
- типа няма деструктор
Копиране
trait Copy
- trait маркер
- указва, че за типа, който го имплементира:
- създаването на копие е еквиваленто на побитово копиране на паметта
- типа няма деструктор
За типове, които са Copy
, не се използва семантика на местенето
- при присвояване те се копират - семантика на копирането
- след копиране все още можем да използваме старата стойност
Копиране
Числените типове имплементират Copy
let n1 = 0xbeef;
let n2 = n1; // копира n1
let n3 = n1.clone(); // еквивалентно на `n3 = n1`
println!("{}", n1);
println!("{}", n2);
println!("Mmm, {:#x}", n3);
48879 48879 Mmm, 0xbeef
fn main() { let n1 = 0xbeef; let n2 = n1; // копира n1 let n3 = n1.clone(); // еквивалентно на `n3 = n1` println!("{}", n1); println!("{}", n2); println!("Mmm, {:#x}", n3); }
Копиране
Споделените референции (&T
, &[T]
, &str
) имплементират Copy
let cookies = String::from("Cookies!");
let s1: &str = &cookies;
let s2: &str = s1;
println!("{}", s1);
println!("Mmm, {}", s2);
Cookies! Mmm, Cookies!
fn main() { let cookies = String::from("Cookies!"); let s1: &str = &cookies; let s2: &str = s1; println!("{}", s1); println!("Mmm, {}", s2); }
Копиране
Споделените референции (&T
, &[T]
, &str
) имплементират Copy
let cookies = String::from("Cookies!");
let s1: &str = &cookies;
let s2: &str = s1;
println!("{}", s1);
println!("Mmm, {}", s2);
Cookies! Mmm, Cookies!
fn main() { let cookies = String::from("Cookies!"); let s1: &str = &cookies; let s2: &str = s1; println!("{}", s1); println!("Mmm, {}", s2); }
- ексклузивните референции (
&mut T
, …) не имплементиратCopy
, защото от тях можем да имаме само една