Começo a aprender Rust

Começo a aprender Rust

Neste final de semana comecei a estudar a linguagem de programação Rust. E para registrar o meu estudo, pretendo escrever alguns textos aqui, começando pelo motivo de ter escolhido esta linguagem, mas depois comentar o que estou achando e dificuldades que estou encontrando.

O principal motivo para eu escolher esta linguagem está ligando as principais linguagens que utilizo, sendo que existia um caso onde elas não atendiam. Para entender a necessidade vou listá-las.

De forma geral utilizo shell script (bash para ser mais específico) quando tenho que fazer automações, ou se já existe algum programa pronto, simplificar o seu uso. Sendo seu maior exemplo o Commit Rules, que permite executar comandos para validar o commit, onde leio o arquivo de configuração, executo alguns comandos e manipulo a saída e exit status dos mesmos.

Quando preciso desenvolver algo mais elaborado, opto pelo Python, que por ser uma linguagem bem geral e de rápido desenvolvimento, facilita bastante o meu trabalho, sendo desde programas em linha de comando, até aplicações web. Vou deixar aqui de exemplo o Deduplicated, que foi desenvolvido como uma biblioteca, tendo tanto uma interface por linha de comando, quanto web.

Porém Python não é uma linguagem que tem foco em performance, especialmente na versão 3. Este foi um dos principais motivos de algumas pessoas da comunidade Python ficarem de olho no Go, o que talvez seria o meu caminho, se tivesse tido necessidade de performance. Também cheguei a pesquisar sobre o Genie, porém por ser recente e a pouca documentação, fizeram eu a deixá-la de lado, até por nem ela, nem Vala terem tido um grande sucesso.

Tenho algum conhecimento de C, porém é mais teórico que prático, não tendo tanta confiança para escrever um software seguro nesta linguagem. Também já tentei estudar mais sobre algumas ferramentas, como Make e Autoconf, porém não encontrei bons materiais para o meu estudo.

Neste ponto encontrei Rust, que pode ser visto como uma alternativa ao C, porém verifica o acesso seguro a memória em tempo de compilação, aumentando minha confiança em escrever um software seguro. Ele também um tem gerenciador de pacotes, que pelos meus testes funcionou muito bem e sem espalhar vários arquivos pelo sistema inteiro, podendo ser excluídos ao apagar um único diretório.

Rust tem outras qualidades, porém esses dois pontos suprem minhas “dificuldades” do C. Se encaixando como uma opção de linguagem quando preciso de desempenho (tenho controle semelhante ao C, e sem garbage collector como outras linguagens), porém ainda é uma linguagem nova com recursos bem interessantes em suas expressões (comandos).

No próximo texto comentarei mais sobre materiais e primeiras impressões.

Como funciona o computador – Argumentos e comunicação de processos

Para um programa funcionar e trazer respostas diferentes é necessário que haja alguma forma de interagir com o mesmo, as vezes o usuário, outras o próprio sistema. Como os sistemas que utilizamos hoje seguem um padrão que é muito forte na linguagem C, irei usá-la como exemplo.

Todos os programas em C, quando chamados diretamente no sistema, iniciam pela função int main(int argc, char *argv[]), para palavra aqui tem um significado. O primeiro e mais óbvio é o main, que é o nome da função, utilizado pelo sistema para saber o ponto inicial de todo processo. Logo antes temos o retorno da função (int), para os processos também conhecido como “return code”, é utilizado para identificar se o programa finalizou com algum erro ou não. Toda vez que a função main chamar um return para finalizar o processo esse número é enviado para quem iniciou o processo, 0 quando tudo foi processado corretamente, ou diferente de 0 caso tenha ocorrido algum erro. Isso é feito dessa forma para que possa identificar a causo do erro, um programa que copia arquivos via rede poderia retornar 0 quando copiou corretamente, 1 quando não conseguiu encontrar o arquivo para copiar e 2 quando tiver alguma falha de rede por exemplo, facilitando assim que outros processos saibam o que ocorreu. No Bash ao finalizar a execução de um programa é possível ver o seu “return code” com o comando echo $?.

Para finalizar a assinatura da função main, temos dois argumentos. argc é um contador de quantos argumentos o programa recebeu na inicialização, normalmente isso é utilizando na linha de comando, por exemplo quando executados cp -R arquivo backup no GNU/Linux, passa 3 argumentos diferentes para o processo. Logo em seguida vem o argv, que é uma lista de onde o valor desses argumentos estão dentro da memória, no caso já descrito para que o programa acesso o -R, arquivo e backup podendo identificar o que o mesmo deverá executar. No Bash esses argumentos podem ser passados depois do nome do programa, separados por espaço.

Muitas vezes em alguma interface gráfica abrimos uma foto, ao dar dois cliques, seria como executar um programa na linha de comando, passando o arquivo como argumento, assim o programa sabe qual arquivo ele deve abrir, como gimp foto.jpg.

Os programas que funcionam na linha de comando também são conhecidos por mostrar textos na tela e as vezes pedir para o usuário digite algumas informações. Isso é possível graça a três fluxos de padrões, STDIN, STDOUT e STDERR, também conhecidos por entrada, saída e saída de erro. Um programa que manipule esses fluxos é capaz de ler o teclado e mostrar mensagens na tela por exemplo. Vale notar que temos dois fluxos de saída, isso permite salvar a saída do programa num arquivo, porém continuar mostrando erros na tela.

No Bash é possível alterar a origem ou destino desses fluxos na execução de códigos. Para que um programa leia um arquivo em vez do teclado, podemos utilizar o <, como em cat < arquivo.txt. Para salvar a saída de um comando num arquivo, podemos utilizar o >, como em find > arquivo.txt, porém caso tenha algum erro ainda será possível ver na tela. Para alterar a saída de erro para um arquivo devemos utilizar o 2>, como find 2> erros.txt. Também podemos juntar esses operadores, como find > arquivo.txt 2> erros.txt. Ainda há uma forma de unir a saída de erro com a saída, utilizando 2>&1, como find > arquivo.txt 2>&1.

Uma das características dos sistemas UNIX são os programas que executam apenas uma função bem definida, fazendo desvios nesses fluxos é possível combinar esses programas para efetuarem coisas maiores, simplesmente conectando a saída de um programa com a entrada de outro, utilizando o |, como cat arquivo.txt | sort | uniq.

Existe ainda uma outra forma de passar argumentos, assim como as linguagens de programação têm variáveis, existem variáveis no contexto do sistema operacional e os processos podem acessá-las, e em alguns casos até alterá-las. No Bash podemos utilizá-las através do export, como export CONFIG=/etc/meu.config e executar o programa posteriormente, ou antes do nome do programa, como CONFIG=/etc/meu.config apache2.

Um exemplo de uso dessas formas de comunicação com processos são os servidores HTTP configurados com CGI, que nada mais são que páginas geradas dinamicamente por um processo. Algumas variáveis referentes ao servidor e cliente são definidas como variáveis de ambiente, formulários e arquivos são enviados para a entrada padrão, o endereço da página acessada inicia um programa específico que processas essas informações e envia para a saída padrão a página HTML que o servidor HTTP responderá para o cliente.

—–

Finalizo aqui esta série, para quem acompanhou espero que tenham gostado.

Não sei se o texto ficou claro o suficiente em todas as partes. O objetivo era apresentar o básico sobre como funciona um computador e como essas partes interagem, podendo assim ter uma visão geral e possibilitar que quem tiver vontade de conhecer mais a fundo possa buscar mais informações. Acredito que esse objetivo tenha sido completado.

Caso surjam mais temas, posso fazer uma segunda série de posts, ou explicar melhor algum ponto, então podem sugerir assuntos.

Como funciona o computador – Linguagem C

Tudo o que o processador faz é executar instruções determinadas que estão na memória, porém escrever essas instruções manualmente é extremamente lento para desenvolver um programa, além de ficar preso a arquitetura de computador a qual foi escrita. A principal alternativa é a linguagem de programação C.

As principais vantagens são: a existência de mecanismos de controle estruturados, permitindo a execução de blocos de comandos cobre certas condições ou até que uma condição se torne falsa sem se preocupar com o endereço das instruções. Apresenta tipos básicos de dados, como número inteiro, decimal, negativo e até caracteres, esses tipos são baseados em alguma codificação para serem representado em bits.

A relação entre a linguagem C e Assembly é bastante direta, uma vez que o GCC (compilador de C do projeto GNU) converte o código para Assembly durante o processo de compilação do programa, como se tudo fosse conjuntos de macros. Porém as instruções em C não têm uma relação muito direta com as instruções Assembly, uma vez que essa relação depende da estratégia adotada pelo compilador, e mesmo compilando um programa duas vezes no mesmo computador pode gerar resultados diferentes.

Existe uma palestra muito interessante sobre compilação determinística que fala justamente do problema dos compiladores gerarem resultados diferentes, e que organizações como a NSA podem tirar proveito para inserir falhas de segurança apenas nos programas compilados que não aparecem no código fonte. Essa palestra está em português e foi apresentada por Seth Schoen no FISL 15 (http://hemingway.softwarelivre.org/fisl15/high/40t/sala40t-high-201405071559.ogv).

Como as instruções em C são padronizadas, um mesmo código poderia ser compilado para diferentes arquiteturas, desde que não tenha instruções ou lógica específica da arquitetura. Para atingir esse objetivo basta mudar o compilador, no caso o GCC permite compilar o programa para uma arquitetura diferente do processador atual, porém tem que ser preparado para isto.

Diferente de outras linguagens, C assume que o programador sabe o que está fazendo e não faz muitas validações no código, desde que esteja dentro da sintaxe da linguagem é possível compilar e rodar posteriormente, porém erros podem ocorrer caso o programador não tenha tratado corretamente a lógica ou valores. Muitas falhas de segurança ocorrem justamente por esse fato, como a falha Heartbleed, que não valida corretamente o tamanho da mensagem enviada pelo cliente e acaba enviando mais informações do que deveria do servidor.

Mesmo sendo bastante antiga é uma das linguagens que mais aproxima o programador com as instruções do processador, por isso muitas vezes ganhando em desempenho e menor consumo de memória que o programa equivalente em outras linguagens. Além dos fatos já descritos, ela também é a linguagem a qual a base dos sistemas operacionais normalmente são escritos e influenciou muitas outras linguagens. Definitivamente não é a mais fácil ou simples de se aprender, porém possibilita um melhor conhecimento do computador e base para conhecer outras linguagens, ou até entender o que ocorre por trás das mesmas.