Executando verificação de segurança...
4
zancho
1 min de leitura ·

[duvidas] Sobre Async Rust

Olá!

Sou novo na área da programação, estou trabalhando na área a apenas 3 anos, atualmente com a linguagem C++.

Recentemente, conheci Rust que é uma linguagem muito interessante, gostei de estudar e criar algumas ferramentas simples.

Porém estava pensando em começar algo mais elaborado, um projeto backend, uma API Rest. Não que o projeto seja grande, mas algo mais bem estruturado.

Então me deparei com uma grande dificuldade, Async Rust e Async Traits, acho que essa é a parte mais complicada da linguagem que vi até agora.

Tentando aplicar uma arquitetura hexagonal, na parte dos repositories por exemplo, não consegui encontrar uma outra opção a não ser usar async-trait, ou seja, uma crate externa onde não deveria ter, o que se propaga para as outras camadas.

Outro problema é que mesmo utilizando essa crate, o código simplesmente não compila quando utilizo workspace, como se essa crate estivesse impedindo a trait de ser vista por outros membros (libs) do projeto.

Então uma abordagem que funcionou foi deixar tudo mais acoplado, no mesmo pacote, mas estruturado em pastas diferentes dentro da src.

Pode ser que eu esteja fazendo algo muito errado e não era para ter essa dificuldade toda, por isso estou pedindo um auxílio aqui.

Queria entender qual a melhor forma de usar essa funcionalidade da linguagem e como estruturar o código, talvez sugestões de projetos open-source bons para utilizar como base.

Carregando publicação patrocinada...
1

Eu nao conheço Arquitetura Hexagonal, mas pode ser um problema similar ao que tive ao tentar aplicar SOLID, Rust não é uma linguagem orientada a objetos (ate tem alguns conceitos mas é forçar a barra...) ela é uma linguagem funcional. Eu comecei a fazer codigo Rust melhor quando entendi que eu precisava entender o conceito próprio de Rust e que nao daria pra aplicar conceitos que eu aplicacava nas linguagens OO que conheço.

1

Realmente, muitos conceitos de outras linguagens orientadas a objetos não se aplicam a Rust, vou buscar entender melhor sobre o próprio conceito da linguagem, valeu pela dica!

1

Uma outra dica, cuidado com unwrap e derivados, rust tem como objetivo criar aplicações que nunca entram em estado de erro, o uso descontrolado de unwrap quebra esse objetivo e no longo prazo faz você deixar de ter confiança no seu próprio código, então por mais chato que seja aplicar um match ou if let com frequência, faça! pq no longo prazo você terá paz mental ao implementar novas features.

1

Bom pra te ajudar melhor seria interessante mostrar o seu projeto, você está usando sqlx, diesel ou fazendo tudo na mão?

Qual o seu problema com o async-trait? Por que acha que ele fere a arquitetura hexagonal?

Eu devo fazer um vídeo num futuro sobre actix, axum, ntex e loco.rs se falar sua dúvida talvez eu já consiga incluir no vídeo =D

Tem essa documentação que é antiga, mas pode te ajudar também.
https://rust-on-nails.com/docs/

1

Estou utilizando sqlx com postgres.

Exemplo simples de estrutura do projeto:

src/application/repositories/company_repository.rs

use crate::core::entities::company::Company;
use async_trait::async_trait;

#[async_trait]
pub trait CompanyRepository {
    async fn update_or_create(&self, company: Company) -> Result<(), String>;
    async fn get_all(&self) -> Result<Vec<Company>, String>;
}

src/adapters/repositories/postgres_company_repository.rs

use crate::application::repositories::company_repository::CompanyRepository;
use async_trait::async_trait;
use sqlx::{Executor, PgPool};

#[async_trait]
impl CompanyRepository for PostgresCompanyRepository {
    async fn update_or_create(&self, company: Company) -> Result<(), String> {
        let model = CompanyModel::from(company);
        let query = sqlx::query(
            "
                INSERT INTO companies (id, social_reason, cnpj)
                VALUES ($1, $2, $3)
                ON CONFLICT (id) DO UPDATE SET social_reason = $2, cnpj = $3
                ",
        )
        .bind(model.id)
        .bind(model.social_reason)
        .bind(model.cnpj);
        self.get_pool()
            .execute(query)
            .await
            .map_err(|e| e.to_string())?;
        Ok(())
    }
}

O problema em questão é que a async_trait está em um arquivo que teoricamente deveria ter apenas dependência do core, sem dependência de third_party.

Mas não encontrei outra opção senão colocar ela aqui, o que me parece errado.

Por isso estou perguntando se existe uma maneira de fazer isso sem quebrar o padrão de dependência do projeto ou devo considerar essa crate como parte da linguagem Rust, porque sem ela ficamos limitados a criar traits síncronas.

A arquitetura deveria permitir essa dependência no arquivo de repositório? Isso que eu considero uma ferida, estou implementando a arquitetura de forma errada?

1

hoje eu acredito que ou voce aceita ou você cria um dyn trait e lidar dentro com o seu projeto com os desafios que a lib implementa.
no roadmap pelo que lembro tem algo pra resolver isso.

Mas, por hora ou implementa com dyn ou usa essa trait.

2
1