fn merge_data(src1: &Vec<u8>, src2: &Vec<u8>) -> Vec<u8> {
let mut output = src1.clone();
output.extend(src2.iter().cloned());
output
}
Na função principal, a chamada padrão funciona conforme esperado:
fn main() {
let text_a = Vec::from(b"Rust ".to_vec());
let text_b = Vec::from(b"rocks!".to_vec());
println!("{}", merge_data(&text_a, &text_b));
}
Flexibilidade inesperada do sistema de tipos
Modifique a declaração de variáveis mantendo a mesma função merge_data:
fn main() {
let text_a = Box::new(Vec::from(b"Rust ".to_vec()));
let text_b = Box::new(Vec::from(b"rocks!".to_vec()));
println!("{}", merge_data(&text_a, &text_b));
}
Apesar da diferença de tipo (&Box<Vec<u8>> vs &Vec<u8>), o código compila e executa corretamente.
Teste com ponteiros compartilhados:
fn main() {
use std::sync::Arc;
let text_a = Arc::new(Vec::from(b"Rust ".to_vec()));
let text_b = Arc::new(Vec::from(b"rocks!".to_vec()));
println!("{}", merge_data(&text_a, &text_b));
}
O compilador aceita &Arc<Vec<u8>> como argumento para um parâmetro &Vec<u8>.
Teste com tipos aninhados profundos
fn main() {
let text_a = Box::new(Box::new(Box::new(Vec::from(b"Rust ".to_vec()))));
let text_b = Box::new(Box::new(Box::new(Vec::from(b"rocks!".to_vec()))));
println!("{}", merge_data(&text_a, &text_b));
}
A mesma flexibilidade persiste mesmo com quatro níveis de aninhamento de Box.
Mecanismo subjacente: Deref Trait em ação
A capacidade de aceitar múltiplos tipos é possível graças à conversão Deref do Rust. Quando um tipo implementa Deref<Target = T>, referências como &Box<T> são automaticamente convertidas para &T.
Para casos específicos, é necessário definir funções com tipos exatos:
fn merge_box(data1: &Box<Vec<u8>>, data2: &Box<Vec<u8>>) -> Box<Vec<u8>> {
let mut result = (*data1).clone();
result.extend(data2.iter().cloned());
Box::new(result)
}
Essa função não aceitará &Arc<Vec<u8>> como argumento, demonstrando que a conversão é unidirecional.
Desafios na inferência de tipos
Em funções genéricas, é necessário espeicficar restrições explícitas:
fn process_data<T, U>(data1: T, data2: U)
where
T: std::ops::Deref<Target = Vec<u8>>,
U: std::ops::Deref<Target = Vec<u8>>
{
let combined = merge_data(&data1, &data2);
// ... processamento adicional
}
Essa abordagem garante que o compilador possa resolver corretamente os tipos durante a inferência.
A implementação do Deref permite que Rust equilibre segurança de memória com conveniência de uso, evitando sobrecarga de código sem comprometer a segurança do sistema.