Como o programar em Rust me faz ver a memória de maneira diferente

Como programar em uma linguagem sem coletor de lixo tem me dado uma outra visão de como programar

rust

O que é o Rust

O Rust é uma linguagem compilada (similar ao C e C++) com o foco de ser concorrente e memory safe, o que é extremamente útil em sistemas onde performance é o que importa. Porém por ser uma linguagem moderna, ela possui algumas facilidades que a tornam maravilhosa, como o seu compilador que tem sempre me ajudado, segue um exemplo que eu vi ele me ajudando como se fosse o resultado de um teste unitário

Erro do compilador

um exemplo de erro do compilador para mostrar como ele é elegante e extremamente prestativo

Testes ?

Em rust possui por padrão testes e o cargo que é o gerenciador de dependências da linguagem. Falarei hoje mais sobre os testes e como são de facil criação e entendimento. Com uma bela documentação o rust não deixa a desejar em nada nesse ponto.

exemplo de código de testes, observe que possui o código #[test] que mostra qual função pode ser usada pra teste. ele espera um retorno para ser true ou false e baseado nele ele passa. o assert_eq! consegue aceitar uma string como seus parametros onde vc pode explicar melhor o teste

    #[test]
    fn abstract_factory_create_car() {
        let car: Box<dyn Vehicle> = factory_method::ShapeFactory::new_vehicle(&VehicleTypes::Car);
        let result: &str = car.translocate();
        assert_eq!(result, "Moved in land", "Creating a car with a type only ");
    }

    #[test]
    fn abstract_factory_create_boat() {
        let boat: Box<dyn Vehicle> = factory_method::ShapeFactory::new_vehicle(&VehicleTypes::Boat);
        let result: &str = boat.translocate();
        assert_eq!(result, "Moved in water", "Creating a boat with a type only ");
    }

    #[test]
    fn abstract_factory_create_plane() {
        let plane: Box<dyn Vehicle> = factory_method::ShapeFactory::new_vehicle(&VehicleTypes::Plane);
        let result: &str = plane.translocate();
        assert_eq!(result, "Moved in air", "Creating a plane with a type only ");
    }

Tem tipos ?

Temos tipos e ponteiros, o que torna ela extremamente expressiva, posso criar meus tipos como ali em Vehicle mas também posso usar tipos da linguagem como em result que eu uso a referencia de um str que é um pedaço de String como no caso " moved in alguma coisa"

Referencia ?

Como uma lang de baixo nível, temos alguns poderes e uma particularidade da linguagem que é maravilhoso. O conceito de References e Borrowing link, esse conceito é extremamente interessante e elegante de se saber. Se você passar uma variável como argumento em uma função, você perdeu a ownership(posse) da variável, o compilador fala a seguinte frase quando você tenta usar ela novamente:

Erro do compilador

Valor já foi movido aqui se você quiser passar de novo você pode passar a referencia dela e alterar os tipos para que isso seja suportado

Como trabalhar com Borrowing ?

Você em rust, pode passar para uma função ao invés de uma variável e mover ela, como o compilador chama, você pode passar a referencia dela e trabalhar lá com ela, mas nunca mutando ou alterando o valor. Sim existe mutabilidade mas você precisa dizer que algo é mutável com a key word mut

Padrões ?

Rust é uma lang com forte inspiração funcional, possui imutabilidade, pattern match e outros traços de linguagens funcionais. Possui contudo modernidades de programação orientada a objetos como traits e strucs e um código muito legível quando procedural

#[derive(Debug)]
pub struct Unique {
    pub global_const: String,
}

/// Get the singleton that is only once initialized
pub fn get_singleton() -> &'static Mutex<Unique> {
    static mut UNIQ: MaybeUninit<Mutex<Unique>> = MaybeUninit::uninit();
    static ONCE: Once = Once::new();
    ONCE.call_once(|| unsafe {
        UNIQ.as_mut_ptr().write(Mutex::new(Unique {
            global_const: "Global string".to_string(),
        }));
    });

    unsafe { &*UNIQ.as_ptr() }
}

pub fn set_singleton(value: &str, singleton_instance: &Mutex<Unique>) {
    let mut new_value = singleton_instance.lock().unwrap();
    new_value.global_const = value.to_string();
}

o Pattern Singleton

use crate::creational::factory_method::VehicleTypes::{Car as CarType, Plane as PlaneType, Boat as BoatType};

pub trait Vehicle {
    fn translocate(&self) -> &str;
}

pub enum VehicleTypes {
    Car,
    Boat,
    Plane,
}

struct Car {}

impl Vehicle for Car {
    fn translocate(&self) -> &str {
        println!("Moved in land");
        "Moved in land"
    }
}


struct Boat {}

impl Vehicle for Boat {
    fn translocate(&self) -> &str {
        println!("Moved in water");
        "Moved in water"
    }
}

struct Plane {}

impl Vehicle for Plane {
    fn translocate(&self) -> &str {
        println!("Moved in air");
        "Moved in air"
    }
}

pub struct ShapeFactory;

impl ShapeFactory {
    pub fn new_vehicle(v: &VehicleTypes) -> Box<dyn Vehicle> {
        match v {
            VehicleTypes::Car => Box::new(Car {}),
            VehicleTypes::Boat => Box::new(Boat {}),
            VehicleTypes::Plane => Box::new(Plane {}),
        }
    }
}

O Pattern de factory

Mais exemplos ?

Por fim gostaria de deixar um exemplo de código que vi na leet code onde pode ser util por mostrar alguns detalhes bem legais da lang que mesmo de baixo nível trabalha com a facilidade que linguagens como o java fariam

    /// given a list of lists which of the list is the biggest sum
    /// as the result of the table!
    ///
    /// _Table_
    /// 
    ///
    /// | Values |   Sum     | Result|  Winner|
    /// |--------|-----------|-------|--------|
    /// | 1 2 3  | 1 + 2 + 3 |  6    |        |
    /// | 5 5 5  | 5 + 5 + 5 |  15   |   X    |
    /// | 3 1 4  | 3 + 1 + 4 |  8    |        |
    /// 
    pub fn biggest_sum(res_: Vec<Vec<i32>>) -> i32 {
        res_.iter()
            .map(|x| x.iter().sum())
            .max()
            .unwrap()
    }

// Teste da função acima
    #[test]
    pub fn biggest_sum_test() {
        let v1 = vec![1, 2 ,3];
        let v2 = vec![5, 5 ,5];
        let v3 = vec![3, 1 ,4];
        let big_vec = vec![v1, v2, v3,];
        let result = biggest_sum(big_vec);

        assert_eq!(result, 15, "Testing sum, should return always 15");
    }

Problema da leet code

Sim, funções terminadas em !(não ele não está falando em voz alta) são macros e isso é um detalhe para outro dia mas que me faz muito feliz. Podemos criar nossos próprios macros que representam funções complexas como o vec![1,2,3] é igual ao Vec::from([1,2,3]), uma maneira mais fácil de inicializar link

Contato

Se quiser discutir sobre qualquer assunto ou viu algum erro, não hesite em me marcar ou chamar no Twitter: @que_cara_legal
Estou sempre tentando trazer o que tenho estudado, as vezes traduzindo algums tópicos divertidos que me chamam atenção.