Importante : para uma leitura confortável do artigo, você precisa ser capaz de ler o código-fonte em Rust e entender por que embrulhar tudo Rc<RefCell<...>>
é ruim.
Introdução
Rust geralmente não é considerada uma linguagem orientada a objetos: não há herança de implementação; à primeira vista, também não há encapsulamento; Finalmente, os gráficos de dependência de objetos mutáveis tão familiares aos adeptos da OOP parecem tão feios quanto possível (basta olhar para todos eles Rc<RefCell<...>>
e Arc<Mutex<...>>
!)
É verdade que a herança de implementação foi considerada prejudicial por vários anos, e os gurus da POO dizem coisas muito corretas como "um bom objeto é um objeto imutável". Então eu me perguntei: como o Object Thinking e Rust realmente se encaixam ?
A primeira cobaia será o padrão de Estado, cuja implementação pura é o assunto deste artigo.
Foi escolhido por um motivo: um capítulo de The Rust Book é dedicado ao mesmo padrão . O objetivo desse capítulo era mostrar que apenas meninos e meninas maus escrevem código orientado a objetos no Rust: aqui você Option
precisa copiar e colar implementações de métodos desnecessários e triviais em todas as implementações do traço. Mas se você aplicar alguns truques, todo o clichê desaparecerá e a legibilidade aumentará.
Escala de trabalho
O artigo original modelou o fluxo de trabalho de uma postagem de blog. Vamos mostrar nossa imaginação e adaptar a descrição original à dura realidade russa:
- Qualquer artigo sobre Habré já foi um rascunho vazio, que o autor teve que preencher com conteúdo.
- Quando o artigo estiver pronto, ele é enviado para moderação.
- Assim que o moderador aprovar o artigo, ele é publicado no Habré.
- Até que o artigo seja publicado, os usuários não devem ver seu conteúdo.
Quaisquer ações ilegais com o artigo não devem ter efeito (por exemplo, você não pode publicar um artigo não aprovado na sandbox).
A lista abaixo demonstra o código correspondente à descrição acima.
// 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
até agora parece com isto:
// 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
}
}
Isso passa por todas as afirmações, exceto a última. Não é ruim!
Implementação do padrão
Vamos adicionar uma característica vazia State
, um estado Draft
e alguns campos para 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(),
}
}
// ...
}
Problemas com cabeça fora Projeto
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, .. .
: , , , . , , ; , .
P.S.: Amethyst.
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<...>>
!
, .