Em Rust, atribuir um valor a um nome designa-se por binding. As bindings são imutáveis, a menos que sejam declaradas com a palavra-chave mut. Como Rust é uma linguagem de tipos estáticos, cada binding tem um tipo conhecido em tempo de compilação.
As bindings são habitualmente definidas com a palavra-chave let. Especificar o tipo de uma binding é opcional para a maioria delas, porque a inferência de tipos de Rust consegue geralmente inferir o tipo a partir do seu valor. Uma binding tem este aspeto:
// Automatically inferred type
let fingers = 10;
As funções são itens. Enquanto as bindings se referem tipicamente a um valor concreto, os itens referem-se a uma unidade de organização do código, normalmente uma função ou um módulo, que está disponível durante todo o tempo de vida do programa. Uma função devolve automaticamente o resultado da sua última expressão. Uma função pode ter 0 ou mais parâmetros, que são bindings com o tempo de vida da chamada da função.
A inferência de tipos é teoricamente possível para funções, mas está desativada por uma decisão intencional de design da linguagem. Isto significa que precisas de gastar um pouco mais de tempo a escrever código para especificar com precisão os tipos dos parâmetros de entrada e do valor devolvido de uma função. Em troca, poupas esse tempo quando lês o código, porque todos esses tipos estão definidos explicitamente.
fn add(x: i32, y: i32) -> i32 {
x + y
}
Invocar uma função faz-se indicando o seu nome seguido de parênteses. Se a função exigir parâmetros, é preciso indicar um argumento para cada um deles dentro dos parênteses.
let five = add(2, 3);
Se o tipo de uma binding não puder ser inferido, o compilador reporta um erro. Para corrigir isto, acrescenta uma anotação de tipo explícita à binding.
// Explicit type annotation
let fingers: i32 = 10;
Os itens em Rust podem ser usados antes ou depois de serem definidos, porque têm um tempo de vida estático. As bindings, por outro lado, só podem ser usadas depois de terem sido definidas. Usar uma binding antes de ela ter sido definida dá origem a um erro de compilação.
fn main() {
// `fn add` hasn't yet been defined, but that's perfectly ok
dbg!(add(3, 4));
}
fn add(x: i32, y: i32) -> i32 {
x + y
}
// this won't compile; `a` is used before its binding is defined
let b = a;
let a = x + y;
Rust usa chavetas ({}) para definir um âmbito. Uma binding definida dentro de um âmbito não pode escapar dele.
let a = 1;
dbg!(a); // 1
{
// Here, we re-bind `a` to a new value, which is still immutable.
// This technique is called _shadowing_. The new binding is constrained to
// this anonymous scope. Outside this scope, the previous binding still
// applies.
let a = 2;
let b = 3;
dbg!(a, b); // 2, 3
}
// can't use `b` anymore because it is out of scope
// dbg!(b);
// The shadowed `a` in the inner scope above has fallen out of scope,
// leaving us with our original binding.
dbg!(a); // 1
Os itens de Rust são muitas vezes organizados em módulos. Cada crate é implicitamente um módulo, mas pode definir submódulos internos com profundidade arbitrária. Um módulo agrupa funcionalidade relacionada e é definido com a palavra-chave mod.
mod calc_i32 {
fn add(a: i32, b: i32) -> i32 { a + b }
fn sub(a: i32, b: i32) -> i32 { a - b }
fn mul(a: i32, b: i32) -> i32 { a * b }
fn div(a: i32, b: i32) -> i32 { a / b }
}
Rust suporta dois tipos de comentários. A palavra-chave // indica um comentário de uma só linha; tudo o que se segue à palavra-chave até ao fim da linha é ignorado. As palavras-chave /* e */ indicam um comentário de várias linhas; tudo o que está entre essas duas palavras-chave é ignorado. É idiomático e boa prática preferir comentários de uma só linha.
Rust também suporta comentários de documentação, que aparecem na documentação gerada pelo cargo doc. Os comentários de documentação externos formam-se com a palavra-chave ///, que funciona exatamente como a palavra-chave //. Aplicam-se ao item que se lhes segue, como uma função:
/// The `add` function produces the sum of its arguments.
fn add(x: i32, y: i32) -> i32 { x + y }
Os comentários de documentação internos formam-se com a palavra-chave //!, que funciona exatamente como a palavra-chave //. Aplicam-se ao item que os envolve, como um módulo:
mod my_cool_module {
//! This module is the bee's knees.
}
Os comentários de documentação podem ter qualquer comprimento e conter markdown, que é renderizado na documentação gerada.
Neste exercício vais escrever algum código para te ajudar a cozinhar uma lasanha fantástica do teu livro de receitas preferido.
Tens quatro tarefas, todas relacionadas com o tempo gasto a cozinhar a lasanha.
Define a ligação expected_minutes_in_oven para verificares quantos minutos a lasanha deve estar no forno. De acordo com o livro de receitas, o tempo previsto no forno é de 40 minutos:
expected_minutes_in_oven()
// Returns: 40
Define a função remaining_minutes_in_oven, que recebe como parâmetro os minutos reais que a lasanha já esteve no forno e devolve quantos minutos a lasanha ainda tem de permanecer no forno, com base no tempo previsto no forno, em minutos, da tarefa anterior.
remaining_minutes_in_oven(30)
// Returns: 10
Define a função preparation_time_in_minutes, que recebe como parâmetro o número de camadas que adicionaste à lasanha e devolve quantos minutos gastaste a preparar a lasanha, assumindo que cada camada demora 2 minutos a preparar.
preparation_time_in_minutes(2)
// Returns: 4
Define a função elapsed_time_in_minutes, que recebe dois parâmetros: o primeiro parâmetro é o número de camadas que adicionaste à lasanha, e o segundo parâmetro é o número de minutos que a lasanha esteve no forno. A função deve devolver quantos minutos passaste a cozinhar a lasanha, ou seja, a soma do tempo de preparação em minutos com o tempo, em minutos, que a lasanha passou no forno até ao momento.
elapsed_time_in_minutes(3, 20)
// Returns: 26
Inscreve-te no Exercism para aprenderes e dominares Rust com 99 exercícios, e mentoria humana real, tudo grátis.