Os tuplos são uma forma leve de agrupar um conjunto fixo de dados de tipos arbitrários. Um tuplo não tem
um nome próprio; dar um nome a uma estrutura de dados transforma-a num struct. Os campos de um tuplo não têm
nome; acede-se a eles por desestruturação ou por posição.
Os tuplos são sempre criados com uma expressão de tuplo:
// pointless but legal
let unit = ();
// single element
let single_element = ("note the comma",);
// two element
let two_element = (123, "elements can be of differing types");
Os tuplos podem ter um número arbitrário de elementos.
É possível aceder aos elementos de um tuplo por desestruturação. Isto significa apenas atribuir nomes de variáveis aos elementos individuais do tuplo, consumindo-o.
let (elem1, _elem2) = two_element;
assert_eq!(elem1, 123);
Também é possível aceder aos elementos de um tuplo através de um índice posicional numérico. A indexação, como sempre, começa no 0.
let notation = single_element.0;
assert_eq!(notation, "note the comma");
Também te vão pedir para trabalhar com structs de tuplo. Tal como as structs normais, estes são tipos com nome; ao contrário das structs normais, têm campos anónimos. A sua sintaxe é muito semelhante à sintaxe normal de tuplos. É possível usar tanto a desestruturação como o acesso posicional.
struct TupleStruct(u8, i32);
let my_tuple_struct = TupleStruct(123, -321);
let neg = my_tuple_struct.1;
let TupleStruct(byte, _) = my_tuple_struct;
assert_eq!(neg, -321);
assert_eq!(byte, 123);
Todos os campos dos tuplos anónimos são sempre públicos. No entanto, os campos das structs de tuplo têm
visibilidade individual, que por defeito é privada, tal como os campos das structs normais. Podes tornar os
campos públicos com o modificador pub, tal como numa struct normal.
// fails due to private fields
mod tuple { pub struct TupleStruct(u8, i32); }
fn main() { let _my_tuple_struct = tuple::TupleStruct(123, -321); }
// succeeds: fields are public
mod tuple { pub struct TupleStruct(pub u8, pub i32); }
fn main() { let _my_tuple_struct = tuple::TupleStruct(123, -321); }
Estás a trabalhar num jogo para um sistema embutido de baixo consumo e precisas de escrever várias funções utilitárias que vão ser usadas por outras partes do jogo.
O quociente é o resultado de uma divisão.
fn divmod(dividend: i16, divisor: i16) -> (i16, i16)
Exemplo:
assert_eq!(divmod(10, 3), (3, 1));
Isto vai ajudar a ativar uma otimização do buffer do ecrã, promete o teu chefe.
Os iteradores são itens que expõem os métodos definidos pela trait Iterator. Essa documentação é bastante extensa, porque oferecem muitos métodos; aqui ficam as propriedades mais relevantes:
enumerate que devolve um tuplo (i, val) para cada valorfilter que usa uma closure para determinar se deve produzir um elemento do iteradormap que usa uma closure para modificar elementos do iteradorComo a tua função pode correr sobre qualquer tipo de iterador, usa impl para indicar que se trata de uma instância de uma trait e não de um item simples. Da mesma forma, a sintaxe <Item=T> significa apenas que não importa que tipo de item o iterador produz; a tua função pode produzir os elementos pares de qualquer iterador.
fn evens<T>(iter: impl Iterator<Item=T>) -> impl Iterator<Item=T>
Exemplos:
let mut even_ints = evens(0_u8..);
assert_eq!(even_ints.next(), Some(0));
assert_eq!(even_ints.next(), Some(2));
assert_eq!(even_ints.next(), Some(4));
assert_eq!(even_ints.next(), Some(6));
let mut evens_from_odds = evens(1_i16..);
assert_eq!(evens_from_odds.next(), Some(1));
assert_eq!(evens_from_odds.next(), Some(3));
assert_eq!(evens_from_odds.next(), Some(5));
assert_eq!(evens_from_odds.next(), Some(7));
Para facilitar o mapeamento, tens uma tuple struct Position:
struct Position(i16, i16);
Precisas de implementar um método manhattan em Position que devolve a distância de Manhattan dessa posição à origem (Position(0, 0)).
impl Position {
fn manhattan(&self) -> i16
}
Exemplo:
assert_eq!(Position(3, 4).manhattan(), 7);
Inscreve-te no Exercism para aprenderes e dominares Rust com 99 exercícios, e mentoria humana real, tudo grátis.