Rust 0.3

Вышла новая версия языка Rust с номером 0.3. Несмотря на то, что говорить о каком бы то ни было коммерческом использовании языка рано, он обретает все более и более четкие формы, появляется понимание куда же он движется.

Автоматический вывод целочисленного типа

В Rust запрещено неявное преобразование между целочисленными типами, поэтому необходимо самостоятельно указывать соответствующий суффикс для всех типов кроме int, например 12_u8. Начиная с версии 0.3 данная система была доработана, и в большинстве ситуаций тип может быть выведен автоматически.

let random_digits: [u8] = [1, 4, 1, 5, 9, 2, 6];

В данном случае мы получим массив восьмибитных беззнаковых целых. В случае использования сопоставления с образцом автоматический вывод типа так же актуален:

let account_balance = 45u64;
alt account_balance {
    0       { log(error, "not enough") }
    1 to 99 { log(error, "enough") }
    _       { log(error, "whoa") }
}

Но, с учетом отсутствия неявного преобразования между типами, возможны конфликтные ситуации, например такие:

let x = 3;

fn identity_u8(n: u8) -> u8 { n }
fn identity_u16(n: u16) -> u16 { n }

identity_u8(x);  // после этого считается что `x` имеет тип `u8`
identity_u16(x); // неверный тип, ожидается `u16`, но найдено `u8`

Стабилизация классов и удаление ресурсов

Концепция ресурсов была признана нежизнеспособной и удалена, на ее замену пришли классы. На данный момент поддержка классов минимальна, тем не менее, имеется поддержка конструкторов, деструкоторов, реализация интерфейсов и поддержка параметризации.

iface talky {
    fn speak();
}
iface printable {
    fn print();
}
class talker<T: printable> : talky {
    let word: T;

    new(word: T) {
        self.word = word;
    }
    drop {
        self.speak();
    }
    fn speak() {
        self.word.print();
    }
}

Новый синтаксис замыканий, ключевое слово do

Замыкания получили более компактный синтаксис:

foo.map( |i| i + 1)

Они работают как с оператором for, так и в do выражениях, реализуя удобный синтаксический сахар для построения функций высшего порядка.

for foo.each |i| {
    println(#fmt("%?", i));
}

При использовании замыканий без аргументов синтаксис еще проще:

do spawn {
    // это тело замыкания
}

Использование ключевого слова do позволяет выносить функции высшего порядка за пределы списка аргументов.

pure fn unpack_slice<T, U>(s: [const T]/& , f: fn(*T, uint) -> U) -> U;

do unpack_slice(rhs) |p, len| {
    for range(0, len) |i| {
        ...
    }
}

Новые типы массивов

До версии 0.3 массивы представляли собой уникальные объекты в куче, но случайное копирование и выделение памяти для массивов было серьезной угрозой для производительности.

Начиная с версии 0.3 массивы получили полную поддержку модели памяти Rust, таким образом, они могут быть расположены на стеке, в локальной памяти или глобальной памяти.

// Уникальный массив (unique vector), располагающийся в глобальной памяти
let x: ~[int] = ~[0];
// Разделяемый массив (shared vector), располагающийся в локальной памяти
let y: @[int] = @[0];
// Стековый массив (stack vector), располагающийся на стеке
let z: &[int] = &[0];

Внимание: на данный момент не все библиотечные функции поддерживают работу с новыми типами массивов.

Паттерн *

В Rust появился новый синтаксис для игнорирования полей в сравнении с образцом:

alt my_enum {
    i_could_match_like_this(_, _, _, _, _, _) {
    }
    but_would_rather_like_this(*) {
    }
}

Doc комментарии

Добавлена поддержка doc комментариев. Как и в случае с другими атрибутами, doc комментарии имеют различную форму в зависимости от расположения.

/** Внешний doc комментарий */
fn f() {
/// Альтернативная форма внешнего doc комментария
fn g() { }
fn h() {
    /*! Внутренний doc комментарий */
}
fn i() {
    //! Альтернативная форма внутренего doc комментария
}

Управление ошибками и предупреждениями компилятора отдельно для каждого элемента

Теперь предупреждения могут быть заблокированы не только на уровне crate (как это перевести?), как это было раньше, но и отдельно для каждой функции.

#[warn(no_non_implicitly_copyable_typarams)]
fn implicitly() -> ~[str] {
    import std::sort::merge_sort;
    merge_sort(str::eq, ~["not_implicitly_copyable"])
}

В данном примере merge_sort будет копировать элементы массива в процессе сортировки, в то же время строка не может быть копирована без задания типа copy (т.к. строки уникальны и их копирование приводит к выделению памяти). В такой ситуации компилятор Rust выдает предупреждение. Так как на данный момент нет механизма уровня языка для разрешения копированя, иногда необходимо просто подавить предупреждение компилятора.

Внимание: текущий синтаксис отключения предупреждений, использующий префикс “no_” с именем предупреждения, вводит в заблуждение и будет изменен в будущем.

Предупреждения (и их настройки по умолчанию), поддерживаемые компилятором Rust:

  • ctypes (warn) – внешние модули должны использовать core::libc типы;
  • unused_imports (ignore) – запретить импортирование неиспользующихся модулей;
  • while_true (warn) – запретить использование while true (необходимо использовать оператор loop);
  • path_statement (warn) – writing a statement that names a value without using it is usually a bug; (как это перевести?)
  • old_vecs (warn) – использование устаревшего синтаксиса создания массивов ([], теперь используется для создания slice (как перевести???), а не для создания уникальных массивов. Текущий синтаксис для создания уникального массива ~[]);
  • old_strs (ignore) – использование устревшего синтаксиса для создания строк
    unrecognized_warning.

Приведенные ниже предупреждения относятся к процессу удаления дорогих операций неявного копирования. В соответствии с текущими правилами, типы, требующие выделения памяти при копировании (unique boxes (как это перевести?)) или содержащие изменяемые поля, не могут копироваться неявно. На данный момент это предупреждения компилятора, которые в дальнейшем будут повышены до ошибок, либо подобный код будет запрещен системой типов Rust по умолчанию.

  • implicit_copys – создание копии типа, не поддерживающего неявное копирование без использования ключевого слова copy;
  • vecs_not_implicitly_copyable – тоже самое, только для векторов;
  • non_implicitly_copyable_typarams – использование параметризуемых типов с copy параметрами для типов данных, неподдерживающих неявное копирование.

Расширения синтаксиса

import io::println;

// Информация о файле
println(#fmt("%?", #line()));
println(#fmt("%?", #col()));
println(#fmt("%?", #file()));

// Имя текущего модуля, если доступно
println(#fmt("%?", #mod()));

let x = 10, y = 15;

// Добавление выражения Rust в строку
println(#fmt("%?", #stringify[x + y]));
// Включение содержимого файла в качестве выражения Rust
println(#fmt("%?", #include("x_plus_y.rs")));
// Включение содержимого файла в качестве строки
println(#fmt("%?", #include_str("x_plus_y.rs")));
// Включение содержимого файла в качестве бинарного массива
println(#fmt("%?", #include_bin("x_plus_y.rs")));

Квалификатор const

В Rust добавлен новый квалификатор const, который может использоваться в качестве ограничения для типов параметров. Тип, который является const, может включать в себя только неизменяемые поля. Тип являющийся как const так и send хорошо подходит для использования в параллельных вычислениях с использованием разделяемой памяти, так как является не изменяемым и не может содержать локальных указателей (local box pointers).
Данный квалификатор используется в модуле core::arc, который реализует автоматический подсчет ссылок и является sendable типом инкапсулирующим const + send квалификаторы.

fn manually_share_arc() {
    // unique vectors are const and send kinds
    let v = ~[1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
    let arc_v = arc::arc(v);

    let p = port();
    let c = chan(p);

    do task::spawn() {
        let p = port();
        c.send(chan(p));

        let arc_v = p.recv();

        let v = *arc::get::<~[int]>(&arc_v);
        assert v[3] == 4;
    };

    let c = p.recv();
    c.send(arc::clone(&arc_v));

    assert (*arc::get(&arc_v))[2] == 3;

    log(info, arc_v);
}

Интерфейс взаимодействия с внешними функциями

Ключевое слово ‘native’ было исключено из языка так как подразумевало что Rust не является нативным. Теперь для описания внешних модулей необходимо использовать ключевое слово extern.

extern mod cairo {

Функции Rust вызываемые из внешнего нативного кода с использованием CDECL ABI, которые раньше описывались при помощи ключевого слова crust, теперь так же описываются ключевым словом extern.

extern fn a_native_callback(user_data: *c_void) {
     do_some_crusty_stuff(user_data);
}

Доработка стандартной библиотеки

В рамках работы над версией 0.3 было добавлено несколько новых методов для базовых типов доступных по умолчанию. В общем случае методы предпочтительнее функций, т.к. появилось четкая концепция работы с self типом.

Так же в состав стандартной библиотеки был добавлен новый модуль time, поддерживаемый Эриком Трайзеларом.

let time: tm = now();

// Преобразование в строку
println(time.strftime("%D/%M/%Y"));

// tm поддерживает различные преобразования
println(time.ctime());   // "Thu Jan  1 00:00:00 1970"
println(time.rfc822());  // "Thu Jan  1 00:00:00 1970"
println(time.rfc3339()); // "2012-02-22T07:53:18-07:00"

Из языка удалено

Ключевые слова be, prove, syntax были удаленны из языка Rust. Ключевое слово mutable трансформировалось в mut, cont переименовано в again. Так как ключевое слово bind имело очень много пересечений с другими формами замыканий, оно так же было удалено. Так как do циклы используются довольно активно, ключевое слово do повторно введено в язык. Тип ресурсы был удален в пользу деструкторов класса.

3 Comments Rust 0.3

  1. NN

    Выглядит интересно , но зачем “fn”, “iface” ?
    На чем экономят ?

    Reply
    1. Alexander Stavonin

      А почему нет? Компактнее программа, легче читать, код не замусориватся кучей длиннющих ключевых слов.

      Reply

Leave a Reply