• Nenhum resultado encontrado

Ao corroborar com os ideais pregados por esse trabalho (confiabilidade e robustez), o RTFM torna- se um dos pilares de fundamentação. O formalismo estático provido pelo SRP oferece às aplicações a certeza que suas propriedades implícitas serão cumpridas, não haverá deadlock nem inversão de prioridade ilimitada. O programador pode, então, se ocupar com as funcionalidades de alto nível. As particularidades envolvendo a utilização da linguagem Rust nos sistemas embarcados são melhor exploradas no próximo Capítulo (4). Nele, mostra-se como as abstrações propostas, idi- omaticamente, ajudam a controlar a complexidade advinda da execução em microcontroladores. Nesse capítulo introduziu-se os motivos que levaram à adoção do RTFM, esses (e suas consequên- cias) norteiam o restante dessa dissertação (Capítulos 5 e 6).

Cap´ıtulo

3

Rust

As linguagens de programação percorreram um longo percurso desde sua criação. Sairam de códigos de máquina para dialetos com grande poder de abstração. As últimas décadas, contudo, ficaram muito marcadas pela dicotomia entre as linguagens “antigas” e as dinâmicas, dando a ilusão de que apenas as interpretadas são mais agradáveis ao desenvolvimento. As linguagens clássicas sofreram com a passagem do tempo e não podem se modificar facilmente já que têm um longo legado a manter.

Otimizações antes da execução e a possível liberdade de sistemas de runtime (como um inter- pretador ou coletor de lixo) tornam as linguagens compiladas mais eficientes, fazendo-se neces- sárias em diversas situações. Aplicações microcontroladas e a computação em nuvem são bons exemplos de utilização dessa família de linguagens, justificando a recente tendência de criação de linguagens com roupagem clássica adaptadas aos novos tempos.

Os mais promissores expoentes são: Rust e Go (GOLANG.ORG, 2019); elas são linguagens ergonômicas, menos verbosas, seguras e adaptadas (PIKE, 2019), oferecendo-se como alternativa a C/C++. A forma empregada para atingir tais objetivos se difere entre os dois idiomas, interessa, ao presente trabalho, somente a linguagem Rust.

Rust é uma nova linguagem de sistema idealizada pela Mozilla. Como C e C++, fornece ao desenvolvedor um bom controle sobre o uso da memória. Entretanto, com uma estreita relação entre as operações da linguagem e o hardware, ajuda os programadores a antecipar os custos de seus códigos (BLANDY, 2015). Rust compartilha algumas ambições com C++, como as abstrações de zero custo, sumarizada por Bjarne Stroustrup: “What you don’t use, you don’t pay for. And

further: What you do use, you couldn’t hand code any better.”. Abstrações não precisam implicar em custos.

A linguagem Rust almeja empoderar a todos para que esses possam construir softwares con- fiáveis e eficientes (RUST LANGUAGE, 2019l); desempenho, confiabilidade e produtividade são as palavras chave. É open source e se orgulha de possuir um ecossistema democrático de desenvol- vimento através de RFCs (Request for Comments). A infraestrutura de compilação fica a cargo da LLVM (LLVM PROJECT, 2019b).

Rust está em constante desenvolvimento e tem um fluxo de lançamento baseado em 3 canais de release: Stable, Beta e Nightly; cada qual com uma função específica. Experimentações são per- mitidas no canal mais instável, Nightly. Como o nome indica esse canal é reconstruído todas as noites, possibilitando que erros sejam descobertos o mais cedo possível. Beta faz o papel de tran- sição entre a experimentação e a estabilidade. Funcionalidades chegam ao Stable após garantias de retrocompatibilidade com versões anteriores (RUST LANGUAGE, 2019g).

3.1 Sistemas Tipos e Segurança

Linguagem segura é aquela que protege suas próprias construções, ou seja, é capaz de garantir a integridade de suas abstrações e das operações inseridas pelos programadores usando definições idiomáticas (PIERCE, 2002). Suponha, por exemplo, uma linguagem que ofereça vetores como estrutura de dados. Um programador usando-a espera que somente as devidas posições reservadas por um vetor lhe sejam acessíveis. Acesso fora da fronteira devem emitir uma indicação de falha. Em uma linguagem segura, as abstrações podem ser acessadas de forma realmente “abstrata”. Em uma linguagem insegura as operações não são confiáveis, não se tem certeza do que irá acon- tecer. É necessário entender profundamente como ela se comporta (PIERCE, 2002) e a utilização equivocada pode resultar no temido comportamento indefinido (BLANDY, 2015). Pela definições acima, Rust é uma linguagem segura.

As Listagens 3.1 e 3.2 demonstram que C++ não é uma linguagem segura. Os programas se diferem apenas na frase inserida na primeira posição do vetor strings. No primeiro exemplo a expressão “é uma string?” está em francês e no segundo em inglês. É imaginável que o resul- tado para os dois casos seja equivalente: a frase acima impressa no relativo idioma. O desfecho,

de pequenas strings - Small String Optimization (SSO) (GOODRICH, 2019). Frases curtas como “Is it a string?” (até 16 caracteres) são armazenadas inline, já a string “Est-ce une chaine?” tem 18 caracteres e não recebe essa otimização. Com a inserção das palavras “Hello” e “World” o vetor ’strings’ excedeu sua capacidade inicial e precisou ser realocado. Ao mesmo tempo, ’first’ continua apontando para o endereço “antigo”. A string em francês não estava “local” ao vetor (já estava o heap), por isso, o Programa 3.1 continua funcionando normalmente. Na versão em in- glês, após o loop, ’first’ não mais aponta para o endereço válido, resultando em comportamento indefinido. Mais detalhes em (MARK ELENDT, 2018).

Um sistema de tipos é um método sintático tratável para provar a ausência de certos com- portamentos classificando as instruções de acordo com os valores que elas computam (PIERCE, 2002). Da maneira como são conhecidos hoje, os type systems foram formalizados na década de 1990, genericamente englobam estudos em lógica, matemática e filosofia. Aqui interessa sua apli- cação à ciência da computação e as vantagens por eles obtidas. Uma linguagem estaticamente tipada é aquela em que essas análises ocorrem em tempo de compilação. As dinamicamente tipa- das (como Python e Scheme) usam sinalizações de runtime para distinguir entre os diversos tipos de estruturas (PIERCE, 2002). Ambos os tipos são capazes de garantir a segurança da linguagem. Rust é forte e estaticamente tipada, ou seja, em tempo de compilação, o tipo de todas as variá- veis deve ser conhecido, e eles não se alteram durante a execução. É interessante notar que isso não implica em perdas de ergonomia. O compilador, observando os valores e casos de utilização, consegue inferir o tipo de grande parte das variáveis, como pode ser visto nas Listagens 3.3, 3.4, 3.5, descritas adiante.

Os sistemas de tipos estáticos são eficientes na detecção de falhas, elucidando cedo alguns er- ros de programação. Quando passam pelo typechecker os programas têm a tendência de “simples- mente funcionar” (PIERCE, 2002). São capturados não apenas erros triviais como a não conversão de string para número antes de obter a raiz quadrada, como também problemas conceituais mais profundos, por exemplo, as unidades matemáticas em um cálculo científico (ELLIS, 2017).

Linguagens tipadas tendem a ser mais eficientes, o compilador pode usar as diferentes repre- sentações para gerar instruções de máquina apropriadas. Além disso, o sistema de tipos é uma poderosa ferramenta de documentação do sistema, oferecendo indicações sobre o comportamento das funções e interfaces. Ao contrário dos comentários e anotações, os tipos são verificados pelo

compilador, então, tem-se garantia que não estão desatualizado (PIERCE, 2002).

Documentos relacionados