Boxilə qiyməti heap-də saxlamaq və rekursiv tip qurmaqRcilə bir neçə sahib,RefCellilə daxili dəyişkənlik yaratmaq- Axınları
moveqapanmaları ilə işə salmaq vəjoinilə nəticələri toplamaq - Kanallar və
Arc<Mutex<T>>ilə axınlar arasında məlumat paylaşmaq
İndiyə qədər hər qiymətin tək sahibi var idi. Amma bəzən başqa şey lazım olur: ölçüsü əvvəlcədən bilinməyən rekursiv struktur, eyni mahnının bir neçə pleylistdə olması, ortaq məlumatı dəyişmək və ya işi prosessorun bir neçə nüvəsinə bölmək. Bunun üçün Rust-da ağıllı göstəricilər və eyni vaxtlı proqramlaşdırma (concurrency) alətləri var — və onlar da sahiblik qaydalarına tabedir.
`Box<T>`: qiymət heap-də
Box::new(x) qiyməti heap-ə yerləşdirir, stekdə isə yalnız sabit ölçülü göstərici qalır. Qutunun sahibi görünmə sahəsindən çıxanda heap-dəki qiymət də avtomatik silinir. Box üç halda lazımdır: ölçüsü kompilyasiya zamanı bilinməyən rekursiv tiplər, Box<dyn Trait> kimi trait obyektləri və stekdə köçürülməsi baha olan böyük məlumat.
enum List {
Node(i32, List),
Empty,
}enum List {
Node(i32, Box<List>),
Empty,
}List öz içində List saxlayır, ona görə kompilyator yazır: recursive type List has infinite size. Box isə sabit ölçülü göstəricidir və dövrəni qırır.enum List {
Node(i32, Box<List>),
Empty,
}
use List::{Empty, Node};
fn sum(list: &List) -> i32 {
match list {
Node(value, next) => value + sum(next),
Empty => 0,
}
}
fn main() {
let boxed = Box::new(5);
println!("boxed + 1 = {}", *boxed + 1);
let list = Node(1, Box::new(Node(2, Box::new(Node(3, Box::new(Empty))))));
println!("sum = {}", sum(&list));
}boxed + 1 = 6 sum = 6
`Rc<T>`: bir neçə sahib
Rc (reference counting — istinadların sayılması) bir qiymətin bir neçə sahibi olmasına imkan verir. Rc::clone(&x) məlumatın surətini çıxarmır, sadəcə sayğacı bir vahid artırır — bu, çox ucuzdur. Hər sahib silinəndə sayğac azalır, sıfıra çatanda isə qiymət silinir. Rc qiyməti yalnız oxumağa icazə verir və yalnız bir axın daxilində işləyir.
use std::rc::Rc;
fn main() {
let song = Rc::new(String::from("lesson.mp3"));
println!("owners: {}", Rc::strong_count(&song));
let playlist_a = Rc::clone(&song);
{
let playlist_b = Rc::clone(&song);
println!("owners: {}", Rc::strong_count(&song));
println!("b plays {playlist_b}");
}
println!("owners: {}", Rc::strong_count(&song));
println!("a plays {playlist_a}");
}owners: 1 owners: 3 b plays lesson.mp3 owners: 2 a plays lesson.mp3
playlist_b daxili blok bitəndə silindi və sayğac 3-dən 2-yə düşdü. Mətnin özü yaddaşda cəmi bir dəfə saxlanılır.`RefCell<T>`: daxili dəyişkənlik
RefCell borc qaydalarını kompilyasiya zamanı yox, proqram işləyərkən yoxlayır. borrow() oxumaq üçün, borrow_mut() dəyişmək üçün borc verir. Bu, dəyişməz istinad arxasındakı qiyməti dəyişməyə imkan verir. Rc<RefCell<T>> birləşməsi tipik həlldir: bir neçə sahib ortaq məlumatı həm oxuya, həm də dəyişə bilir.
use std::cell::RefCell;
use std::rc::Rc;
fn main() {
let scores = Rc::new(RefCell::new(vec![80, 95]));
let teacher = Rc::clone(&scores);
teacher.borrow_mut().push(70);
scores.borrow_mut().push(100);
println!("{:?}", scores.borrow());
let total: i32 = teacher.borrow().iter().sum();
println!("total = {total}");
}[80, 95, 70, 100] total = 345
| Tip | Sahiblər | Dəyişmək | Axınlar arası paylaşma |
|---|---|---|---|
Box<T> | bir | sahib mut-dursa | bəli |
Rc<T> | bir neçə | yox | yox |
RefCell<T> | bir | bəli, işləmə zamanı yoxlanır | yox — başqa axına köçürmək olar, paylaşmaq olmaz (Sync deyil) |
Arc<T> | bir neçə | yox | bəli |
Arc<Mutex<T>> | bir neçə | bəli, kilid ilə | bəli |
Axınlar (threads)
thread::spawn qapanmanı yeni axında işə salır və JoinHandle qaytarır. join() axının bitməsini gözləyir və onun nəticəsini verir. Qapanmanın qarşısındakı move istifadə etdiyi dəyişənlərin sahibliyini axına ötürür. Diqqət: axınların öz daxilindən çap etdiyi sətirlərin sırası hər işə salmada fərqli ola bilər. Ona görə aşağıda axınlar nəticə qaytarır, main isə onları sabit sırada çap edir.
use std::thread;
fn main() {
let mut handles = Vec::new();
for id in 1..=3_u64 {
let handle = thread::spawn(move || {
let sum: u64 = (1..=id * 1000).sum();
(id, sum)
});
handles.push(handle);
}
for handle in handles {
let (id, sum) = handle.join().unwrap();
println!("thread {id}: sum = {sum}");
}
}thread 1: sum = 500500 thread 2: sum = 2001000 thread 3: sum = 4501500
use std::thread;
fn main() {
let names = vec!["Aysel", "Murad"];
let handle = thread::spawn(|| {
println!("{:?}", names);
});
handle.join().unwrap();
}use std::thread;
fn main() {
let names = vec!["Aysel", "Murad"];
let handle = thread::spawn(move || {
println!("{:?}", names);
});
handle.join().unwrap();
}closure may outlive the current function, but it borrows names. Axın main-dən uzun yaşaya bilər, ona görə istinad kifayət deyil. Sağdakı proqram ["Aysel", "Murad"] çap edir.Kanallar və `Arc<Mutex<T>>`
Axınlar arasında məlumatı iki üsulla paylaşırlar. Birincisi kanaldır: mpsc::channel() ötürücü (tx) və qəbuledici (rx) yaradır, bir axın send ilə göndərir, digəri alır. Qəbuledicini for ilə gəzmək olar — bütün ötürücülər silinəndə dövr bitir. Tək göndərənin mesajları göndərildiyi sırada gəlir.
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
let worker = thread::spawn(move || {
for step in ["download", "unpack", "install"] {
tx.send(step).unwrap();
}
});
for message in rx {
println!("got: {message}");
}
worker.join().unwrap();
println!("done");
}got: download got: unpack got: install done
İkinci üsul ortaq vəziyyətdir. Arc — Rc-nin axınlar üçün təhlükəsiz (atomar) variantıdır, Mutex isə kilidir: lock() çağıran axın məlumata tək başına sahib olur, qaytarılan qoruyucu (guard) görünmə sahəsindən çıxanda kilid avtomatik açılır. Birlikdə Arc<Mutex<T>> bir neçə axının eyni qiyməti növbə ilə dəyişməsinə imkan verir.
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let counter = Arc::new(Mutex::new(0));
let mut handles = Vec::new();
for _ in 0..10 {
let counter = Arc::clone(&counter);
handles.push(thread::spawn(move || {
let mut n = counter.lock().unwrap();
*n += 1;
}));
}
for handle in handles {
handle.join().unwrap();
}
println!("Result: {}", *counter.lock().unwrap());
}Result: 10
Arc-ın öz surətini alır — bu, kölgələmə ilə yazılıb: let counter = Arc::clone(&counter);.Əsas fikirlər
Box<T>qiyməti heap-də saxlayır; rekursiv tiplər vədyn Traitüçün lazımdır.Rc<T>bir axında bir neçə sahib verir;Rc::cloneyalnız sayğacı artırır.RefCell<T>borc qaydalarını işləmə zamanı yoxlayır; pozuntu panikaya səbəb olur.thread::spawn(move || ...)axın yaradır,join()nəticəni gözləyir; çapı sabit sıradamain-də et.- Kanallar mesaj ötürür,
Arc<Mutex<T>>isə axınlar arasında ortaq dəyişən vəziyyət verir.
Özünü yoxla
10 sual. Hər düzgün cavab XP qazandırır.
enum List { Node(i32, List), Empty } niyə kompilyasiya olunmur?