Присвояване, копиране, референции
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..endstart....end..
Низове и резени
Интервали
- началото и краят могат да се изпуснат
start..endstart....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, защото от тях можем да имаме само една