Сборка Rust из репозитория

Очень часто, особенно в случае с рабочими машинами, доступ в интернет довольно жестко ограничен и почти все протоколы за исключением HTTP(S) заблокированы. Именно с такой проблемой я и столкнулся пытаясь собрать Rust. Все дело в том, что кроме основного модуля кодовой базы, который можно загрузить по HTTPS, у проекта есть два дополнительных подмодуля, для которых жестко заданна работа через протокол Git. В результате, в процессе сборки, я столкнулся с ошибкой:

configure: git: submodule status
-1170ffba3ac5191930b40c897d4569a9d8a296a3 src/libuv
-3a57b672f89adcb2d2d06adc564dc15ca4e276d6 src/llvm
configure: git: submodule update
fatal: unable to connect to github.com:
github.com[0: 207.97.227.239]: errno=Connection timed out

Continue reading

Классы в Rust

В Rust, зачем-то запихали классы. Видимо для того, что бы было “как у всех”. Ощущения от классов, мягко говоря, противоречивые. Хотя в рассылке и утверждают, что с тем, как классы будут выглядеть еще не определились, но ничто так не постоянно, как все временно. Итак, выглядит это дело следующим образом:

class test_class {
    priv {
        let mut val: int;
    }
    new(val: int) {
        self.val = val;
    }
    fn foo() {
        self.val += 1;
        io::println(#fmt("val+1 == %d", self.val));
    }
}

fn main() {
    let obj = test_class(10);
    obj.foo();
}

Любой класс должен иметь конструктор, в противном случае, компилятор скажет что-то типа следующего:

class.rs:14:0: 14:2 error: class with no ctor

В то же время, никаких подтверждений наличию деструкторов я не нашел. Полагаю, будут, но позже, а сейчас можно воспользоваться типом resource, пусть и будет выглядеть итоговое решение крайне криво.
Вторая не понравившаяся особенность классов, необходимость использовать self для доступа к членам из функций классов. Так же self используется для доступа к функциям класса, что выглядит как некий атавизм.
А в целом… Ну, классы, такие классы…

Перегрузка функций по параметрам. Часть 2.

В результате обсуждения в мэйл-листе Rust вопроса о перегрузке функций, родился еще один довольно занятный вариант:

enum input { int(int), str(str) }

iface to_input { fn to_input() -> input; }
impl of to_input for int { fn to_input() -> input { ret int(self); } }
impl of to_input for str { fn to_input() -> input { ret str(self); } }

fn to_input<T: to_input>(t: T) {
    alt t.to_input() {
        int(v) { io::println("int"); }
        str(v) { io::println("str"); }
    }
}

fn main() {
    to_input(5);
    to_input("hello")  
}

В принципе, выгляди это дело крайне громоздко, но, я думаю, что количество кода можно существенно подсократить засчет использования макросов.

Управление памятью в Rust. Часть 2.

К сожалению, а может и к счастью, Rust не предоставляет возможности создать не управляемый объект в хипе. Так или иначе, объект будет управляться при помощи либо разделяемых, либо уникальных указателей. И в первом и во втором случаях, по входу последнего из указателей за пределы области видимости объект в памяти будет уничтожен. В довольно редких случаях, такое поведение не подходит. В качестве примера можно взять данные, передаваемые в качестве параметра для асинхронной функции в неуправляемую библиотеку.
Для решения подобной задачи остается только одно, выделить память при помощи malloc и не забыть освободить ее при помощи free. Ну и небольшой примерчик того, как облегчить себе жизнь.

unsafe fn mk_mem_obj_copy<T>(src: T) -> *T {
    let size = sys::size_of::<T>();
    let dst = libc::malloc(size);
    libc::memcpy(dst, ptr::addr_of(src) as *libc::c_void, size);
    ret dst as *T;
}

unsafe fn mk_mem_obj<T>() -> *T {
    libc::malloc(sys::size_of::<T>()) as *T
}

unsafe fn mem_obj_free<T>(obj: *T) {
    libc::free( obj as *libc::c_void );
}

Управление памятью в Rust.

Управление памятью в Rust построено довольно необычно, поэтому хотелось бы написать про него пару слов. Для начала приведу описание структуры, которая будет использоваться для наглядных экспериментов:

type test_rec = {
    fld1: uint,
    fld2: str,
    fld3: char
};

Эксперимент 1. В руководстве по Rust сказано, что компилятор самостоятельно выбирает способ передачи параметров в функцию. Данные небольшого размера, например int копируются, данные большего размера, например записи, неявно передаются по ссылке.
Continue reading

Перегрузка функций по параметрам

В Rust, по релизиозно-техническим причинам не хотят добавлять перегрузку функций по параметрам. Фактически, доступен только вариант из Си с созданием кучи функций с разными именами. На мой взгляд, это просто дико не удобно. Немного подумав, нашел не очень красивый, но работающий обходной вариант:

enum foo_param {
    i(int),
    s(str)
}

fn foo(param: foo_param) {
    alt param {
        i(int_val) {
            io::println(#fmt("foo was called with int == %d", int_val));
        }
        s(str_val) {
            io::println(#fmt("foo was called with str == %s", str_val));
        }
    }
}

fn main() {
    foo(i(10));
    foo(s("test"));
}

Continue reading

Когнитивный диссонанс

Вчера я был поражен на столько, что усомнился в том, во что верил уже лет 10. А именно в том, что при выравнивании 8, размер структур с полями {u64, u32, u16} и {u8, u32, u16} будет одинаков, т.е. равен 16. А ведь приложение собранное Rust утверждает что в первом случае размер 16, что и следовало ожидать, а во втором 12(!!!).

type tuple_type1 = (u64, u32, u16);
type tuple_type2 = (u8, u32, u16);

fn main() {
    io::println(#fmt("size of tuple_type1 = %u, size of tuple_type2 = %u",
        sys::size_of::<tuple_type1>(), sys::size_of::<tuple_type2>()));
    io::println(#fmt("align of tuple_type1 = %u, align of tuple_type2 = %u",
        sys::align_of::<tuple_type1>(), sys::align_of::<tuple_type2>()));
}

Содержимое консоли:

> ./main
size of tuple_type1 = 16, size of tuple_type2 = 12
align of tuple_type1 = 8, align of tuple_type2 = 8

Continue reading

Юнит-тесты и Rust

ИДея написания интегрированных юнит-тестов в Rust мне очень понравилась. Суть в том, что код тестов пишется не где-то сбоку, а в одном и том же файле что и основной рабочий код. И это еще не все! В процессе сборке, при помощи ключа компилятора –test можно запустить их на выполнение и посмотреть результаты их работы. Причем, совершенно не важно то, что у модуля нет функции main, она будет доавленна автоматически.
На практике это выглядит так:

fn foo() -> uint {
    ret 42;
}

#[test]
fn test_foo() {
    let foo_val = foo();

    assert foo_val != 0u;
}

Собираем и смотрим:

> rustc --test test.rc
> ./test
running 1 tests
test test_foo ... ok

result: ok. 1 passed; 0 failed; 0 ignored

На мой взгляд, реализация тестирования в Rust претендует на гордое звание идеальная!

**char to str

С документацией по Rust на данный момент не очень, зато в почтовой рассылке rust-dev@mozilla.org на вопросы отвечают очень быстро. Пусть и дают примеры с ошибками
Сейчас я пишу враппер для libevent, правильнее сказать начал писать. С первой же функцией, которую я выбрал как наиболее простую и ни на что не завязанную, я натолкнулся на вопрос с преобразованием массива Си строк в массив Rust строк.
Для начала пишем прототип функции event_get_supported_methods из libevent и объясняем Rust с какой библиотекой линковаться.

import libc::c_char;

#[link_name = "event"]
native mod c {
    fn event_get_supported_methods() -> **libc::c_char;
}

Теперь сам метод, где нам требуется перекодировка: Continue reading