Несколько дней назад я начал знакомиться с новым языком Rust, разрабатываемым в Mozilla Labs. В конце января вышла версия 0.1, которую можно скачать и попробовать. Предварительную версию спецификации я прочитал ещё осенью 2011 года и заинтересовался этим языком. Отчасти это связано просто с интересом к новым языкам, отчасти — с некоторым разочарованием в языке Go. Уже прошло два года, а в Go по-прежнему нет generics. К тому же есть много мелочей (например, табуляция вместо пробелов), каждая из которых ни на что не влияет, но вместе они оставляют неприятное впечатление.
К языку Rust у меня наоборот сразу сложилось приятное отношение:
- Он создаётся как системный язык (статическая типизация, больший контроль над памятью, взаимодействие с языком C), а значит у программ на нём есть возможность быть быстрыми
- Он имеет необходимые средства функционального программирования (замыкания, алгебраические типы, сопоставление с образцом, классы типов, неизменяемые данные по умолчанию)
- Есть средства для ООП (интерфейсы, сокрытие реализации, изменяемые объекты)
- Есть особенности, полезные для крупных проектов (компиляция и зависимости на уровне сборок, реализация интерфейса для типа отдельно от объявления типа, обобщённое программирование, система пакетов)
Присутствует в языке Rust и пара изюминок: относительно редкая пока многозадачность через обмен сообщениями без разделяемых данных в стиле Erlang, а также уникальная система typestate — статически проверяемые состояния типов, в которых разрешены те или иные операции с ними. Эта концепция раньше встречалась только в экспериментальных языках. С её помощью можно на пользовательском уровне решать проблемы уникальных указателей, неинициализированных переменных и ряд других. Стоит заметить, что именно эти проблемы решены с помощью typestate внутри компилятора, а для использования на уровне пользователя языка представляется пока лишь некоторая часть возможностей typestate.
Наконец, особенности языка, влияющие на эмоциональное впечатление: приятный краткий синтаксис, хорошие конвенции форматирования кода и именования, аккуратный читаемый код стандартной библиотеки, отзывчивое сообщество.
Думаю, я ещё напишу об этом языке. А пока что для создания первого впечатления
ниже я привожу программу на Rust, вычисляющую с помощью его стандартной
библиотеки std
контрольную сумму SHA-1 от файла. Она работает аналогично
стандартной утилите sha1sum
.
Вот эта программа, файл sha1sum.rs
:
use std;
import std::io;
import result::{ok,err};
import std::sha1::{sha1,mk_sha1};
fn reader_sha1sum(reader: io::reader) -> sha1 {
const bufsize: uint = 4096u;
let sha1 = mk_sha1();
while !reader.eof() {
sha1.input(reader.read_bytes(bufsize));
}
ret sha1;
}
fn main(args: [str]) {
if vec::len(args) != 2u {
fail "usage: sha1sum FILE";
}
let filename = args[1];
alt io::file_reader(filename) {
ok(reader) {
let sha1 = reader_sha1sum(reader);
io::println(#fmt("%s %s", sha1.result_str(), filename));
}
err(msg) { fail msg; }
}
}
#[test]
fn test_sha1_byte_buf() {
let reader = io::string_reader("Привет, мир!");
let expected = "0cb95b7891ff182f0972be8754ec934df65af21c";
let sha1 = reader_sha1sum(reader);
assert sha1.result_str() == expected;
}
Всю работу выполняет функция fn reader_sha1sum(reader: io::reader) -> sha1
,
возвращающая объект с хранимой контрольной суммой для входящего файлового
потока. Вообще, интерфейс io::reader
здесь немного излишний. Достаточно было
бы интерфейса для итерации по последовательности из байтовых массивов [u8]
. В
языке есть средства для этого, но стандартная библиотека итерации ещё не
закончена, а реализовывать этот интерфейс прямо в коде я не стал, чтобы не
усложнять пример.
Можно либо просто скомпилировать программу (тогда будет выполняться fn
main(args: [str])
), либо скомпилировать тесты в ней при помощи опции
компилятора --test
(тогда выполнятся функции, проаннотированные с помощью
#[test]
. Кстати, в стандартной библиотеке есть пока ошибка в получении
строкового представления контрольной суммы, так что тест не пройдёт.
Вот так-то, интересный язык!