Присвояване, копиране, референции
13 октомври 2026
Административни неща
- регистрирайте се на сайта https://fmi.rust-lang.bg
Структури
struct User {
email: String,
sign_in_count: u32,
}
let user1 = User {
email: "user@example.com".to_string(),
sign_in_count: 0,
};
fn main() {
struct User {
email: String,
sign_in_count: u32,
}
let user1 = User {
email: "user@example.com".to_string(),
sign_in_count: 0,
};
}
Структури
struct User {
email: String,
sign_in_count: u32,
}
let user1 = User {
email: "user@example.com".to_string(),
sign_in_count: 0,
};
println!("имейл: {}, влизания: {}", user1.email, user1.sign_in_count);
имейл: user@example.com, влизания: 0
fn main() {
struct User {
email: String,
sign_in_count: u32,
}
let user1 = User {
email: "user@example.com".to_string(),
sign_in_count: 0,
};
println!("имейл: {}, влизания: {}", user1.email, user1.sign_in_count);
}
Структури
struct User {
email: String,
sign_in_count: u32,
}
let user1 = User {
email: "user@example.com".to_string(),
sign_in_count: 0,
};
println!("потребител: {:?}", user1);
error[E0277]: `User` doesn't implement `Debug` --> src/bin/main_a1661ab76f33beb5d4f2d7f48e9583162333d530.rs:12:30 | 12 | println!("потребител: {:?}", user1); | ---- ^^^^^ `User` cannot be formatted using `{:?}` because it doesn't implement `Debug` | | | required by this formatting parameter | = help: the trait `Debug` is not implemented for `User` help: consider annotating `User` with `#[derive(Debug)]` | 2 + #[derive(Debug)] 3 | struct User { | For more information about this error, try `rustc --explain E0277`. error: could not compile `rust` (bin "main_a1661ab76f33beb5d4f2d7f48e9583162333d530") due to 1 previous error
fn main() {
struct User {
email: String,
sign_in_count: u32,
}
let user1 = User {
email: "user@example.com".to_string(),
sign_in_count: 0,
};
println!("потребител: {:?}", user1);
}
Структури
#[derive(Debug)]
struct User {
email: String,
sign_in_count: u32,
}
let user1 = User {
email: "user@example.com".to_string(),
sign_in_count: 0,
};
println!("потребител: {:?}", user1);
потребител: User { email: "user@example.com", sign_in_count: 0 }
fn main() {
#[derive(Debug)]
struct User {
email: String,
sign_in_count: u32,
}
let user1 = User {
email: "user@example.com".to_string(),
sign_in_count: 0,
};
println!("потребител: {:?}", user1);
}
Структури
derive
#[derive(Debug)]
struct User {
- анотация
- специален вид макрос
- имплентира някаква фунционалност за структурата
- в случая възможност да се принтира в репрезентация за дебъгване (
{:?})
Собственост
Преместване
let user1 = User {
email: "foo@example.com".to_string(),
sign_in_count: 0,
};
println!("user1: {:?}", user1);
user1: User { email: "foo@example.com", sign_in_count: 0 }
fn main() {
#[derive(Debug)]
struct User { email: String, sign_in_count: u32 }
let user1 = User {
email: "foo@example.com".to_string(),
sign_in_count: 0,
};
println!("user1: {:?}", user1);
}
- променливата
user1има собственост (ownership) над стойносттаUser { ... }
Собственост
Преместване
let user1 = User {
email: "foo@example.com".to_string(),
sign_in_count: 0,
};
let user2 = user1;
println!("user2: {:?}", user2);
user2: User { email: "foo@example.com", sign_in_count: 0 }
fn main() {
#[derive(Debug)]
struct User { email: String, sign_in_count: u32 }
let user1 = User {
email: "foo@example.com".to_string(),
sign_in_count: 0,
};
let user2 = user1;
println!("user2: {:?}", user2);
}
- можем да прехвърлим стойността на друга променлива
Собственост
Преместване
let user1 = User {
email: "foo@example.com".to_string(),
sign_in_count: 0,
};
let user2 = user1;
println!("user2: {:?}", user2);
user2: User { email: "foo@example.com", sign_in_count: 0 }
fn main() {
#[derive(Debug)]
struct User { email: String, sign_in_count: u32 }
let user1 = User {
email: "foo@example.com".to_string(),
sign_in_count: 0,
};
let user2 = user1;
println!("user2: {:?}", user2);
}
- можем да прехвърлим стойността на друга променлива
- след присвояването променливата
user2има собственост над стойността
Собственост
Преместване
let user1 = User {
email: "foo@example.com".to_string(),
sign_in_count: 0,
};
let user2 = user1;
println!("user2: {:?}", user2);
user2: User { email: "foo@example.com", sign_in_count: 0 }
fn main() {
#[derive(Debug)]
struct User { email: String, sign_in_count: u32 }
let user1 = User {
email: "foo@example.com".to_string(),
sign_in_count: 0,
};
let user2 = user1;
println!("user2: {:?}", user2);
}
- можем да прехвърлим стойността на друга променлива
- след присвояването променливата
user2има собственост над стойността - след присвояването променливата
user1не съдържа стойност
Собственост
Преместване
След присвояване старата променлива не може да се достъпва.
Опит за това връща компилационна грешка: use of moved value.
let user1 = User {
email: "foo@example.com".to_string(),
sign_in_count: 0,
};
let user2 = user1;
println!("user2: {:?}", user2);
println!("user1: {:?}", user1);
error[E0382]: borrow of moved value: `user1` --> src/bin/main_f8a53c25bfbf58232d883fce5edc9e6360290642.rs:13:25 | 5 | let user1 = User { | ----- move occurs because `user1` has type `User`, which does not implement the `Copy` trait ... 10 | let user2 = user1; | ----- value moved here ... 13 | println!("user1: {:?}", user1); | ^^^^^ value borrowed here after move | note: if `User` implemented `Clone`, you could clone the value --> src/bin/main_f8a53c25bfbf58232d883fce5edc9e6360290642.rs:3:1 | 3 | struct User { email: String, sign_in_count: u32 } | ^^^^^^^^^^^ consider implementing `Clone` for this type ... 10 | let user2 = user1; | ----- you could clone this value For more information about this error, try `rustc --explain E0382`. error: could not compile `rust` (bin "main_f8a53c25bfbf58232d883fce5edc9e6360290642") due to 1 previous error
fn main() {
#[derive(Debug)]
struct User { email: String, sign_in_count: u32 }
let user1 = User {
email: "foo@example.com".to_string(),
sign_in_count: 0,
};
let user2 = user1;
println!("user2: {:?}", user2);
println!("user1: {:?}", user1);
}
Собственост
Преместване
- преместването на стойност е една от основните семантики в езика
- move semantics
Собственост
Преместване
- преместването на стойност е една от основните семантики в езика
- move semantics
- валидна е за всеки тип
- не може да се забрани
- не може да се предефинира
Собственост
Преместване
- преместването на стойност е една от основните семантики в езика
- move semantics
- валидна е за всеки тип
- не може да се забрани
- не може да се предефинира
Преместването не се води модифициране на стойността - не изисква mutable binding.
let user1 = ...; // няма `mut`
let user2 = user1;
Собственост
Преместване
Преместването може да се използва за да променим дали дадена стойност е зад mutable или immutable binding.
…
let user = ...;
let mut user2 = user;
let mut user = ...;
let user2 = user;
Собственост
Преместване
Преместването може да се използва за да променим дали дадена стойност е зад mutable или immutable binding.
(Това е един добър повод за ползване на засенчване на променливи.)
let user = ...;
let mut user = user;
let mut user = ...;
let user = user;
Собственост
Клониране
Вместо да преместваме стойността, можем да направим копие с метода clone().
Копирането е експицитно.
let email1 = String::from("foo@example.com");
let email2 = email1.clone();
println!("email1: {}", email1);
println!("email2: {}", email2);
email1: foo@example.com email2: foo@example.com
fn main() {
let email1 = String::from("foo@example.com");
let email2 = email1.clone();
println!("email1: {}", email1);
println!("email2: {}", email2);
}
Собственост
Клониране
Типове дефинирани от нас по подразбиране не поддържат clone().
…
//
struct User { email: String, sign_in_count: u32 }
let user1 = User {
email: "foo@example.com".to_string(),
sign_in_count: 0,
};
let user2 = user1.clone();
error[E0599]: no method named `clone` found for struct `User` in the current scope --> src/bin/main_ef76d58f80b783af858ae2f9af0b8ae3232baf3f.rs:9:19 | 3 | struct User { email: String, sign_in_count: u32 } | ----------- method `clone` not found for this struct ... 9 | let user2 = user1.clone(); | ^^^^^ method not found in `User` | = help: items from traits can only be used if the trait is implemented and in scope = note: the following trait defines an item `clone`, perhaps you need to implement it: candidate #1: `Clone` For more information about this error, try `rustc --explain E0599`. error: could not compile `rust` (bin "main_ef76d58f80b783af858ae2f9af0b8ae3232baf3f") due to 1 previous error
fn main() {
//
struct User { email: String, sign_in_count: u32 }
let user1 = User {
email: "foo@example.com".to_string(),
sign_in_count: 0,
};
let user2 = user1.clone();
}
Собственост
Клониране
Типове дефинирани от нас по подразбиране не поддържат clone().
Ако искаме, трябва да го имплементираме с derive(Clone).
#[derive(Clone)]
struct User { email: String, sign_in_count: u32 }
let user1 = User {
email: "foo@example.com".to_string(),
sign_in_count: 0,
};
let user2 = user1.clone();
fn main() {
#[derive(Clone)]
struct User { email: String, sign_in_count: u32 }
let user1 = User {
email: "foo@example.com".to_string(),
sign_in_count: 0,
};
let user2 = user1.clone();
}
Собственост
Клониране
Типове дефинирани от нас по подразбиране не поддържат clone().
Ако искаме, трябва да го имплементираме с derive(Clone).
#[derive(Clone, Debug)]
struct User { email: String, sign_in_count: u32 }
let user1 = User {
email: "foo@example.com".to_string(),
sign_in_count: 0,
};
let user2 = user1.clone();
println!("user1: {:?}", user1);
println!("user2: {:?}", user2);
user1: User { email: "foo@example.com", sign_in_count: 0 } user2: User { email: "foo@example.com", sign_in_count: 0 }
fn main() {
#[derive(Clone, Debug)]
struct User { email: String, sign_in_count: u32 }
let user1 = User {
email: "foo@example.com".to_string(),
sign_in_count: 0,
};
let user2 = user1.clone();
println!("user1: {:?}", user1);
println!("user2: {:?}", user2);
}
Собственост
Копиране
Някои типове поддръжат семантика на копирането.
При тях присвояването води до копиране на стойността.
След това и старата и новата променлива могат да се използват.
let n1 = 123;
let n2 = n1;
println!("{}", n1);
println!("{}", n2);
123 123
fn main() {
let n1 = 123;
let n2 = n1;
println!("{}", n1);
println!("{}", n2);
}
Собственост
Копиране
- семантика на копирането (copy semantics)
Собственост
Копиране
- семантика на копирането (copy semantics)
- основно за примитивни типове - числа, booleans, …
Собственост
Копиране
- семантика на копирането (copy semantics)
- основно за примитивни типове - числа, booleans, …
- тези типове се наричат
Copyтипове, защото имплементират traitCopy
- тези типове се наричат
Собственост
Копиране
- семантика на копирането (copy semantics)
- основно за примитивни типове - числа, booleans, …
- тези типове се наричат
Copyтипове, защото имплементират traitCopy
- тези типове се наричат
- изисква клонирането (
clone()) да е еквивалентно на побитово копие на стойността
Референции
Референции
- указател
- non-null
- винаги валиден
Референции
- референция
&T- споделена референция / константна референция
- shared reference / immutable borrow
- тип
&T - взимане на референция:
let ref_x = &x; - дереференциране:
let x_value = *ref_x;
Референции
fn print_user(user: &User) {
println!("потребител {} (влизания {})", user.email, user.sign_in_count);
}
let user1 = User {
/* ... */
};
print_user(&user1);
print_user(&user1);
потребител foo@example.com (влизания 444) потребител foo@example.com (влизания 444)
fn main() {
#[derive(Debug)]
struct User { email: String, sign_in_count: u32 }
fn print_user(user: &User) {
println!("потребител {} (влизания {})", user.email, user.sign_in_count);
}
let user1 = User {
/* ... */
email: "foo@example.com".to_string(),
sign_in_count: 444,
};
print_user(&user1);
print_user(&user1);
}
Референции
fn print_user(user: &User) {
println!("потребител {} (влизания {})", user.email, user.sign_in_count);
}
let user1 = User {
/* ... */
};
let user_ref: &User = &user1;
print_user(user_ref);
print_user(user_ref);
потребител foo@example.com (влизания 444) потребител foo@example.com (влизания 444)
fn main() {
#[derive(Debug)]
struct User { email: String, sign_in_count: u32 }
fn print_user(user: &User) {
println!("потребител {} (влизания {})", user.email, user.sign_in_count);
}
let user1 = User {
/* ... */
email: "foo@example.com".to_string(),
sign_in_count: 444,
};
let user_ref: &User = &user1;
print_user(user_ref);
print_user(user_ref);
}
Референции
- референция
&mut T- ексклузивна референция / mutable референция
- unique reference / mutable borrow
- тип
&mut T - взимане на референция:
let ref_x = &mut x; - дереференциране:
let value_x = *ref_x;
Референции
fn check_and_sign_in(user: &mut User, email: &str) -> bool {
if user.email == email {
user.sign_in_count += 1;
true
} else {
false
}
}
let mut user1 = User {
/* ... */
};
print_user(&user1);
check_and_sign_in(&mut user1, "foo@example.com");
print_user(&user1);
потребител foo@example.com (влизания 0) потребител foo@example.com (влизания 1)
fn main() {
#[derive(Debug)]
struct User { email: String, sign_in_count: u32 }
fn print_user(user: &User) { println!("потребител {} (влизания {})", user.email, user.sign_in_count); }
fn check_and_sign_in(user: &mut User, email: &str) -> bool {
if user.email == email {
user.sign_in_count += 1;
true
} else {
false
}
}
let mut user1 = User {
/* ... */
email: "foo@example.com".to_string(),
sign_in_count: 0,
};
print_user(&user1);
check_and_sign_in(&mut user1, "foo@example.com");
print_user(&user1);
}
Валидност
Lifetime на променлива - напълно стандартно:
- променливите са валидни до края на лексикографския scope
- на края на scope-а се извиква деструктор (ако има) и стойността се унищожава
- в обратния ред на декларирането им
fn main() {
let user1 = User { /* ... */ };
} // <---- `user1` се унищожава
Валидност
Lifetime на референиця:
- референцията се води жива до последното и използване
fn main() {
let user1 = User { /* ... */ }; // --------+
// |
let ref1 = &user1; // ---------------+ |
let ref2 = &user1; // ---------------+--+ |
// | | |
println!("first: {:?}", ref1); // | | |
// <-+ | |
println!("seconds: {:?}", ref2); // | |
// <----+ |
} // <----------------------------------------+
Валидност
- референцията е указател - адрес в паметта
Валидност
- референцията е указател - адрес в паметта
- който не е null
Валидност
- референцията е указател - адрес в паметта
- който не е null
- който винаги сочи към валидна стойност
- не е преместена или деалокиана след вземането на референцията
Валидност
- референцията е указател - адрес в паметта
- който не е null
- който винаги сочи към валидна стойност
- не е преместена или деалокиана след вземането на референцията
- референциите в Rust са винаги валидни!
Валидност
- не можем да преместим стойност, ако съществува референция към нея
- защото това би инвалидирало референцията
let user = User {
/* ... */
};
let user_ref = &user;
// грешка: cannot move out of `user` because it is borrowed
let new_user = user;
println!("{:?}", user_ref);
error[E0505]: cannot move out of `user` because it is borrowed --> src/bin/main_fa66e6edff560b5c8aaa445e44adbf1d5f1dcaaf.rs:14:16 | 5 | let user = User { | ---- binding `user` declared here ... 11 | let user_ref = &user; | ----- borrow of `user` occurs here ... 14 | let new_user = user; | ^^^^ move out of `user` occurs here 15 | 16 | println!("{:?}", user_ref); | -------- borrow later used here | help: consider cloning the value if the performance cost is acceptable | 11 | let user_ref = &user.clone(); | ++++++++ For more information about this error, try `rustc --explain E0505`. error: could not compile `rust` (bin "main_fa66e6edff560b5c8aaa445e44adbf1d5f1dcaaf") due to 1 previous error
#[derive(Clone, Debug)]
struct User { email: String, sign_in_count: u32 }
fn main() {
let user = User {
/* ... */
email: "foo@example.com".to_string(),
sign_in_count: 0,
};
let user_ref = &user;
// грешка: cannot move out of `user` because it is borrowed
let new_user = user;
println!("{:?}", user_ref);
}
Валидност
- предишния пример се компилира, ако премахнем последния
println
let user = User {
/* ... */
};
let user_ref = &user;
// `user_ref` не се използва след този ред
// преместване от `user` - OK
let new_user = user;
// println!("{:?}", user_ref);
#[derive(Clone, Debug)]
struct User { email: String, sign_in_count: u32 }
fn main() {
let user = User {
/* ... */
email: "foo@example.com".to_string(),
sign_in_count: 0,
};
let user_ref = &user;
// `user_ref` не се използва след този ред
// преместване от `user` - OK
let new_user = user;
// println!("{:?}", user_ref);
}
Валидност
- референцията не може да надживява стойността, към която сочи
fn main() {
let user1 = User { /* ... */ };
{
let user_ref = &user1;
} // <--+
// | OK - user_ref се "унищожава" преди user1
}
Валидност
- референцията не може да надживява стойността, към която сочи
fn main() {
let user1 = User { /* ... */ };
let user_ref = &user1;
} // <--+
// | OK - user_ref се "унищожава" преди user1
Валидност
- референцията не може да надживява стойността, към която сочи
fn main() {
let user_ref = {
let user1 = User { /* ... */ };
&user1
}; // <--+
// | грешка - user1 се унищожава преди user_ref
}
Валидност
- референцията не може да надживява стойността, към която сочи
fn main() {
let empty_email = String::new();
let mut email_ref = &empty_email[..];
{
let user1 = User {
/* ... */
};
email_ref = &user1.email;
};
println!("{}", email_ref);
}
error[E0597]: `user1.email` does not live long enough --> src/bin/main_7c4924a93304e9d9eaba1247515b3e982b24fef8.rs:13:21 | 7 | let user1 = User { | ----- binding `user1` declared here ... 13 | email_ref = &user1.email; | ^^^^^^^^^^^^ borrowed value does not live long enough 14 | }; | - `user1.email` dropped here while still borrowed 15 | 16 | println!("{}", email_ref); | --------- borrow later used here For more information about this error, try `rustc --explain E0597`. error: could not compile `rust` (bin "main_7c4924a93304e9d9eaba1247515b3e982b24fef8") due to 1 previous error
struct User { email: String, sign_in_count: u32 }
fn main() {
let empty_email = String::new();
let mut email_ref = &empty_email[..];
{
let user1 = User {
/* ... */
email: "".to_string(),
sign_in_count: 0,
};
email_ref = &user1.email;
};
println!("{}", email_ref);
}
Валидност
- референцията не може да надживява стойността, към която сочи
fn main() {
let empty_email = String::new();
let mut email_ref = &empty_email[..];
{
let user1 = User {
/* ... */
};
email_ref = get_user_email(&user1);
};
println!("{}", email_ref);
}
fn get_user_email(user: &User) -> &str {
&user.email
}
error[E0597]: `user1` does not live long enough --> src/bin/main_d31014a3fdf35ce3c6dbef96c1002b613d0e4934.rs:13:36 | 7 | let user1 = User { | ----- binding `user1` declared here ... 13 | email_ref = get_user_email(&user1); | ^^^^^^ borrowed value does not live long enough 14 | }; | - `user1` dropped here while still borrowed 15 | 16 | println!("{}", email_ref); | --------- borrow later used here For more information about this error, try `rustc --explain E0597`. error: could not compile `rust` (bin "main_d31014a3fdf35ce3c6dbef96c1002b613d0e4934") due to 1 previous error
struct User { email: String, sign_in_count: u32 }
fn main() {
let empty_email = String::new();
let mut email_ref = &empty_email[..];
{
let user1 = User {
/* ... */
email: "".to_string(),
sign_in_count: 0,
};
email_ref = get_user_email(&user1);
};
println!("{}", email_ref);
}
fn get_user_email(user: &User) -> &str {
&user.email
}
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 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);
}
}
}
Низове
Низове
String
Stringе тип, поддържащ собственост над динамично алокирано парче памет- чието съдържание е utf8 закодиран низ
- типа
Stringе структура с полетаptrlencap
Низове
Разполагане в паметта (String)
let greeting = String::from("Hello, Rust");
fn main() {
let greeting = String::from("Hello, Rust");
}
[–––– greeting: String ––––]
+––––––––+––––––––+––––––––+
stack frame │ ptr=• │ cap=16 │ len=11 │
+–––––│––+––––––––+––––––––+
+–––+
│
│
[–│–––––––––––––––––– ptr + len ––––––––––––]
[-│------------------ ptr + cap --------------------------------]
+–V–+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+
heap │ H │ e │ l │ l │ o │ , │ ␣ │ R │ u │ s │ t │ │ │ │ │ │
+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+
Низове
String
- динамичен низ
- методи като
push(),pop() - съдържа буфер с определен капацитет
- автоматично се реалокира при превишаване на капацитета
let mut hello = String::from("Hello, ");
hello.push('w');
hello.push_str("orld!");
println!("{:?}", hello);
"Hello, world!"
fn main() {
let mut hello = String::from("Hello, ");
hello.push('w');
hello.push_str("orld!");
println!("{:?}", hello);
}
Низове
&str
&strе тип референция- указател към някакво парче памет
- чието съдържание е utf8 закодиран низ
- съдържа
ptrlen
Низове
Разполагане в паметта (&str)
let greeting = String::from("Hello, Rust");
let greeting_slice = greeting.as_str();
fn main() {
let greeting = String::from("Hello, Rust");
let greeting_slice = greeting.as_str();
}
[–––– greeting: String ––––] [greeting_slice: &str]
+––––––––+––––––––+––––––––+ +–––––––––+–––––––––+
stack frame │ ptr=• │ cap=16 │ len=11 │ │ ptr=• │ len=11 │
+–––––│––+––––––––+––––––––+ +––––––│––+–––––––––+
+––––+ │
│ +–––––––––––––––––––––––––––––––––+
│ │
│ │
[│–│––––––––––––––––– str ––––––––––––––––––]
+V–V+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+
heap │ H │ e │ l │ l │ o │ , │ ␣ │ R │ u │ s │ t │ │ │ │ │ │
+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+
Низове
&str
&str- string slice- immutable резен от низ
- през него може само да се прочете низа
Низове
&str
&str- string slice- immutable резен от низ
- през него може само да се прочете низа
&mut str- mutable string slice- mutable резен от низ
- през него може да се променя съдържанието на низа (отделни символи)
- но не могат да се махат или добавят символи
Низове
Литерали
- типа на низовете литерали също е
&str
let s: &str = "literal string";
Низове
Литерали
- типа на низовете литерали също е
&str
let s: &str = "literal string";
- те сочат към памет, която се притежава от "програмата"
- т.е. памет, която е заделена от операционната система
- при зареждане на програмата от файл в паметта
Низове
Литерали
- типа на низовете литерали също е
&str
let s: &str = "literal string";
- те сочат към памет, която се притежава от "програмата"
- т.е. памет, която е заделена от операционната система
- при зареждане на програмата от файл в паметта
let s: &'static str = "Hello";
- всъщност пълния тип е
&'static str, но това е тема за друг път
Низове
Разполагане в паметта (&str)
let greeting = "Hello, Rust";
let who = &greeting[7..];
fn main() {
let greeting = "Hello, Rust";
let who = &greeting[7..];
}
[– greeting: &str –] [–– who: &str ––]
+––––––––+––––––––+ +–––––––+–––––––+
stack frame │ ptr=• │ len=11 │ │ ptr=• │ len=4 │
+–––––│––+––––––––+ +–––––│–+–––––––+
+–––+ │
│ +–––––+
│ │
│ │
│ [–│––– str –––––]
+–V–+–––+–––+–––+–––+–––+–––+–V–+–––+–––+–––+
.rodata │ H │ e │ l │ l │ o │ │ ␣ │ R │ u │ s │ t │
+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+
Низове
Резени
- резенът може да сочи към част от низ
let s = "Hello, world";
let hello = &s[0..5];
let world = &s[7..12];
println!("{:?}", s);
println!("{:?}", hello);
println!("{:?}", world);
"Hello, world" "Hello" "world"
fn main() {
let s = "Hello, world";
let hello = &s[0..5];
let world = &s[7..12];
println!("{:?}", s);
println!("{:?}", hello);
println!("{:?}", world);
}
Низове
Резени
- това не води до заделяне на нови низове
- само референция към вече алокирана памет
let s = "Hello, world";
let hello = &s[0..5];
let world = &s[7..12];
println!("{:12}: ptr={:#x} len={}", s, s.as_ptr() as usize, s.len());
println!("{:12}: ptr={:#x} len={}", hello, hello.as_ptr() as usize, hello.len());
println!("{:12}: ptr={:#x} len={}", world, world.as_ptr() as usize, world.len());
Hello, world: ptr=0x5627861b307b len=12 Hello : ptr=0x5627861b307b len=5 world : ptr=0x5627861b3082 len=5
fn main() {
let s = "Hello, world";
let hello = &s[0..5];
let world = &s[7..12];
println!("{:12}: ptr={:#x} len={}", s, s.as_ptr() as usize, s.len());
println!("{:12}: ptr={:#x} len={}", hello, hello.as_ptr() as usize, hello.len());
println!("{:12}: ptr={:#x} len={}", world, world.as_ptr() as usize, world.len());
}
Низове
Интервали
- интервал
0..5- тип
Range - полуотворен интервал
[0; 5) = {0, 1, 2, 3, 4}
- тип
Низове
Интервали
- интервал
0..5- тип
Range - полуотворен интервал
[0; 5) = {0, 1, 2, 3, 4}
- тип
- интервал
0..=5- тип
RangeInclusive - затворен интервал
[0; 5] = {0, 1, 2, 3, 4, 5}
- тип
Низове
Интервали
- интервал
0..5- тип
Range - полуотворен интервал
[0; 5) = {0, 1, 2, 3, 4}
- тип
- интервал
0..=5- тип
RangeInclusive - затворен интервал
[0; 5] = {0, 1, 2, 3, 4, 5}
- тип
- други типове интервали
..5- от началото до 5 (без 5)..=5- от началото до 5 (включително)5..- от 5 до края..- от началото до края
Низове
Интервали
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);
}
Низове
Използване
Кога да използвате String?
- когато ви трябва да създадете нова стойност
- когато ви трябва да запазите стойност за по-дълго
struct User {
first_name: String,
last_name: String,
email: String,
}
Низове
Използване
Кога да използвате &str?
- когато ви трябва само да прочетете дадена стойност
fn print_names(first: &str, last: &str) { /* ... */ }
- когато резултата е част от входните данни
fn trim_whitespace(s: &str) -> &str { /* ... */ }
Низове
Използване
format!макрото може да се използва за построяване на низ- поддържа същите placeholders като
println!
let hello = "hello";
let hello2 = format!("{}, again", hello); // : String
println!("{:?}", hello2);
"hello, again"
fn main() {
let hello = "hello";
let hello2 = format!("{}, again", hello); // : String
println!("{:?}", hello2);
}
Низове
Използване
// конвертиране от &str до String
let s = String::from("hello");
let s = "hello".to_string();
let s = "hello".to_owned();
// конвертиране от String до &str
let str_ref = s.as_str();
let str_ref = &s[..];
fn main() {
// конвертиране от &str до String
let s = String::from("hello");
let s = "hello".to_string();
let s = "hello".to_owned();
// конвертиране от String до &str
let str_ref = s.as_str();
let str_ref = &s[..];
}
Низове
Индексиране
str::lenвръща дължината на низ в брой байтове, не символи- при взимането на подрезен се оказват брой байтове
- за работа по символи трябва да се използва итератора
chars()
let hello = "Здравей свят";
println!("bytes {}", hello.len());
println!("chars {}", hello.chars().count());
println!("{:?}", &hello[..14]);
bytes 23 chars 12 "Здравей"
fn main() {
let hello = "Здравей свят";
println!("bytes {}", hello.len());
println!("chars {}", hello.chars().count());
println!("{:?}", &hello[..14]);
}
Низове
Индексиране
&strнизовете трябва винаги да съдържат валиден utf8- затова не е позволено всимане на резен по средата на символ
let hello = "Здравей свят";
println!("{:?}", &hello[..13]);
thread 'main' (10012) panicked at src/bin/main_3b57e62b952095839146965c2229e9511f8e078c.rs:4:24: end byte index 13 is not a char boundary; it is inside 'й' (bytes 12..14 of string) note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
fn main() {
let hello = "Здравей свят";
println!("{:?}", &hello[..13]);
}
Низове
Индексиране
&strняма дефинирана операци за индексиране с число
let hello = "Здравей свят";
println!("{:?}", hello[2]);
error[E0277]: the type `str` cannot be indexed by `{integer}` --> src/bin/main_07697788709a48545e5e9b9d2fbc0083c5254bc1.rs:4:24 | 4 | println!("{:?}", hello[2]); | ^ string indices are ranges of `usize` | = help: the trait `SliceIndex<str>` is not implemented for `{integer}` = note: you can use `.chars().nth()` or `.bytes().nth()` for more information, see chapter 8 in The Book: <https://doc.rust-lang.org/book/ch08-02-strings.html#indexing-into-strings> help: `usize` implements trait `SliceIndex<T>` --> /home/nikola/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/slice/index.rs:179:1 | 179 | const unsafe impl<T> SliceIndex<[T]> for usize { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `SliceIndex<[T]>` | ::: /home/nikola/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/bstr/traits.rs:197:1 | 197 | unsafe impl SliceIndex<ByteStr> for usize { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `SliceIndex<ByteStr>` = note: required for `str` to implement `Index<{integer}>` For more information about this error, try `rustc --explain E0277`. error: could not compile `rust` (bin "main_07697788709a48545e5e9b9d2fbc0083c5254bc1") due to 1 previous error
fn main() {
let hello = "Здравей свят";
println!("{:?}", hello[2]);
}
Масиви
| Масив | Низ | ||
|---|---|---|---|
| статичен | [T; N] |
- | собственост над стойността |
| динамичен | Vec<T> |
String |
собственост над стойността |
| резен | &[T] |
&str |
заета назаем стойност (borrow) |
| mutable резен | &mut [T] |
&mut str |
заета назаем стойност (borrow) |
Live coding
Пример за четене от стандартния вход