Penting : untuk membaca artikel dengan nyaman, Anda harus bisa membaca kode sumber di Rust dan memahami mengapa membungkus semuanya Rc<RefCell<...>>itu buruk.
pengantar
Rust umumnya tidak dianggap sebagai bahasa berorientasi objek: tidak ada warisan implementasi; pada pandangan pertama, tidak ada enkapsulasi juga; Akhirnya, grafik ketergantungan dari objek yang bisa berubah yang begitu familiar bagi ahli OOP terlihat seburuk mungkin (lihat saja semua ini Rc<RefCell<...>>dan Arc<Mutex<...>>!)
Benar, pewarisan implementasi telah dianggap berbahaya selama beberapa tahun, dan ahli OOP mengatakan hal-hal yang sangat benar seperti "objek yang baik adalah objek yang tidak dapat diubah". Jadi saya bertanya-tanya: Seberapa baik Obyek Berpikir dan Rust benar-benar cocok bersama-sama ?
Kelinci percobaan pertama adalah pola Negara Bagian, penerapan murni yang merupakan subjek artikel ini.
Itu dipilih karena alasan: satu bab dari The Rust Book dikhususkan untuk pola yang sama . Tujuan dari bab itu adalah untuk menunjukkan bahwa hanya anak laki-laki dan perempuan nakal yang menulis kode berorientasi objek di Rust: di sini Anda Optionperlu menyalin dan menempelkan penerapan metode yang tidak perlu dan sepele ke dalam semua penerapan sifat tersebut. Tetapi jika Anda menerapkan beberapa trik, seluruh boilerplate akan hilang dan keterbacaan akan meningkat.
Skala kerja
Artikel asli memodelkan alur kerja entri blog. Mari tunjukkan imajinasi kita dan sesuaikan deskripsi asli dengan realitas Rusia yang keras:
- Setiap artikel di Habré pernah menjadi draf kosong, yang harus diisi oleh penulis dengan konten.
- Ketika artikel sudah siap, itu dikirim untuk moderasi.
- Segera setelah moderator menyetujui artikel tersebut, maka artikel tersebut akan diterbitkan di Habré.
- Hingga artikel dipublikasikan, pengguna tidak boleh melihat isinya.
Tindakan ilegal apa pun dengan artikel tersebut tidak akan berpengaruh (misalnya, Anda tidak dapat menerbitkan artikel yang tidak disetujui dari kotak pasir).
Daftar di bawah ini menunjukkan kode yang sesuai dengan deskripsi di atas.
// main.rs
use article::Article;
mod article;
fn main() {
let mut article = Article::empty();
article.add_text("Rust -");
assert_eq!(None, article.content());
article.send_to_moderators();
assert_eq!(None, article.content());
article.publish();
assert_eq!(Some("Rust -"), article.content());
}
Article sejauh ini terlihat seperti ini:
// article/mod.rs
pub struct Article;
impl Article {
pub fn empty() -> Self {
Self
}
pub fn add_text(&self, _text: &str) {
// no-op
}
pub fn content(&self) -> Option<&str> {
None
}
pub fn send_to_moderators(&self) {
// no-op
}
pub fn publish(&self) {
// no-op
}
}
Ini melewati semua pernyataan kecuali yang terakhir. Tidak buruk!
Penerapan pola
Mari tambahkan sifat kosong State, keadaan Draftdan beberapa bidang ke Article:
// article/state.rs
pub trait State {
// empty
}
// article/states.rs
use super::state::State;
pub struct Draft;
impl State for Draft {
// nothing
}
// article/mod.rs
use state::State;
use states::Draft;
mod state;
mod states;
pub struct Article {
state: Box<dyn State>,
content: String,
}
impl Article {
pub fn empty() -> Self {
Self {
state: Box::new(Draft),
content: String::new(),
}
}
// ...
}
Bermasalah dengan menghadang rancangan
State, . , - :
trait State {
fn send_to_moderators(&mut self) -> &dyn State;
}
, , , — .
?
pub trait State {
fn send_to_moderators(&mut self) -> Box<dyn State>;
}
. . , ?
:
pub trait State {
fn send_to_moderators(self: Box<Self>) -> Box<dyn State>;
}
: ( self). , Self: Sized, .. . trait object, .. .
: , , , . , , ; , .
use crate::article::Article;
pub trait State {
fn send_to_moderators(&mut self) -> Transit {
Transit(None)
}
}
pub struct Transit(pub Option<Box<dyn State>>);
impl Transit {
pub fn to(state: impl State + 'static) -> Self {
Self(Some(Box::new(state)))
}
pub fn apply(self, article: &mut Article) -> Option<()> {
article.state = self.0?;
Some(())
}
}
, , Draft:
// article/states.rs
use super::state::{State, Transit};
pub struct Draft;
impl State for Draft {
fn send_to_moderators(&mut self) -> Transit {
Transit::to(PendingReview)
}
}
pub struct PendingReview;
impl State for PendingReview {
// nothing
}
// article/mod.rs
impl Article {
// ...
pub fn send_to_moderators(&mut self) {
self.state.send_to_moderators().apply(self);
}
// ...
}
-
: Published, State, publish PendingReview. Article::publish :)
. content State, Published , , Article:
// article/mod.rs
impl Article {
// ...
pub fn content(&self) -> Option<&str> {
self.state.content(self)
}
// ...
}
// article/state.rs
pub trait State {
// ...
fn content<'a>(&self, _article: &'a Article) -> Option<&'a str> {
None
}
}
// article/states.rs
impl State for Published {
fn content<'a>(&self, article: &'a Article) -> Option<&'a str> {
Some(&article.content)
}
}
, ? , !
impl Article {
// ...
pub fn add_text(&mut self, text: &str) {
self.content.push_str(text);
}
// ...
}
( ) , .
! !
, Article , - , , . ? , ! .
, - Rust , , . - -.
, , Rust . , Observer: , Arc<Mutex<...>>!
, .