?

Log in

No account? Create an account

Previous Entry | Next Entry

Rust

На днях вышла 1.0.0.alpha, решил наконец приобщиться. Почитал онлайн книжку. Если она не слишком много скрывает, язык довольно маленький и простой, это хорошо. Для начала сделал вариант для недавнего микробенчмарка про маленький интерпретатор. В тот раз добрые люди помогли ускорить наивные решения, так что все времена опустились ниже 1 секунды, что делает замеры менее осмысленными. Тем не менее, вот текущие результаты:

D - 0.40 s (при использовании LDC)
Rust - 0.44 s
OCaml - 0.57 s
Haskell - 0.85 s
(с одной закавыкой - Rust тут 64-битный, все остальные 32-битные, так уж получилось)

Т.к. опыт с Rust'ом у меня пока минимальный, впечатления смутные. Одной фразой - "ML в руках плюсовиков". Видишь знакомый набор из алгебраиков, паттерн-матчинга, лямбд, expression-based syntax, начинаешь писать как на ML, и тут на тебя выпрыгивает наследие С++: а ты здесь это значение насовсем передал (у нас move-семантика по-умолчанию, уж больно нам эта фича из С++ понравилась) или хотел лишь по указателю? Ах по указателю, тогда так и напиши везде, и где принимаешь, и где передаешь. А еще и писать туда хотел? Тогда не забудь при передаче &mut дописать. И это же выскакивает при паттерн-матчинге: вот тут ты поле алгебраика заматчил, тебе его так отдать или по ссылке? А обращаться хорошо с ней будешь?
Причем как-то странно сделано, вот есть структуры и есть туплы, разница между ними довольно косметическая, так? Можем пару значений передать как тупл, а можем как структуру. Сделаем пару одинаковых функций, складывающих два поля:
#[derive(Show)]
struct S { x : i32, y : i32 }

fn eat_struct(s : S) -> i32 { 
  s.x + s.y 
}

fn eat_tuple(t : (i32, i32)) -> i32 { 
  let (x,y) = t;
  x + y
} 

Обе получают аргумент по значению.
Теперь попробуем их повызывать:
fn main() {
  let s = S { x: 1, y : 2 };
  let t = (1, 2);
  let rs = eat_struct(s);
  let rt = eat_tuple(t);
  println!("{} {} {:?} {:?}", rs, rt, s, t);
}

И получаем ошибку:
hi.rs:18:39: 18:40 error: use of moved value: `s`
hi.rs:18   println!("{} {} {:?} {:?}", rs, rt, s, t);
                                               ^

Оказывается, когда мы структуру передали в ту функцию, мы ее отдали насовсем, это был move. А вот тупл скопировался, передача по значению, оригинал остается у вызывающей ф-ии. Неожиданно.

Еще занятный момент. В растовском варианте, что по ссылке выше, есть такое выражение:
1 + (if a[i] > a[j] { evalBlock(a, b1) } else { evalBlock(a, b2) })
Казалось бы, его, как в ML вариантах, можно заменить более простым:
1 + evalBlock(a, if a[i] > a[j] { b1 } else { b2 })
Но не тут-то было. Rust считает, что в первом аргументе evalBlock происходит мутабельное заимствование массива а, а при вычислении второго аргумента имеет место иммутабельное заимствование этого же массива. И хотя аргументы должны быть вычислены до вызова функции, и по времени эти два использования массива не пересекаются никак, Rust считает, что тут два параллельных заимствования, одно из которых мутабельное, что недопустимо.

Буду продолжать наблюдения. В целом штука занятная.

Tags:

Comments

( 51 comments — Leave a comment )
macrop
Jan. 14th, 2015 05:21 am (UTC)
ммм.. как вводу. сижу голову над с++ перещениями ломаю. Как раз rust вспоминал вечером))
kodt_rsdn
Jan. 14th, 2015 07:36 am (UTC)

Стобы понять с++ные перемещения, надо знать предысторию. С их велосипедами народ трахался пятнадцать лет, пока не пришёл к пониманию ценности, цены, области применения и стандарта.


С передачей ссылок и значений, кстати, народ также натрахался вволю, но ещё в фортране.

(Deleted comment)
diam_2003
Jan. 14th, 2015 05:24 am (UTC)
Да-да, вот эти самые пляски с владением в Rust-е с теоретической стороны кажутся весьма привлекательными, особенно для программистов на С++, а на деле порой нетривиально замутняют код. К вопросу о том, почему не всё, что делает компилятор (например, escape-анализ) следует пихать в систему типов.
macrop
Jan. 14th, 2015 07:36 am (UTC)
У меня раньше пляска со строками была.
Были адовы горы строк с подсчётом ссылок. Но при этом, иногда их надо было просто передавать куда-то для вывода или обработки, не дёргая попусту всю эту мишуру с механизмом подсчёта. Поэтому была какая-то припарка, вспомогательный объект, через который в функцию передавался объект, без пересчёта ссылок. Очень это геморно было, и от ошибок не защищёно.

Не понимаю как в си раньше до такого нужного механизма не додумались. В Rust, я так понимаю, он более развит, хотя не все проблемы решает.
(no subject) - diam_2003 - Jan. 14th, 2015 07:41 am (UTC) - Expand
(no subject) - thedeemon - Jan. 14th, 2015 08:32 am (UTC) - Expand
(no subject) - diam_2003 - Jan. 14th, 2015 08:45 am (UTC) - Expand
(no subject) - thedeemon - Jan. 14th, 2015 09:16 am (UTC) - Expand
(no subject) - diam_2003 - Jan. 14th, 2015 10:35 am (UTC) - Expand
(no subject) - wizzard0 - Jan. 14th, 2015 01:27 pm (UTC) - Expand
(no subject) - nivanych - Jan. 14th, 2015 01:37 pm (UTC) - Expand
(no subject) - geniepro - Jan. 15th, 2015 02:45 am (UTC) - Expand
(no subject) - diam_2003 - Jan. 15th, 2015 04:58 am (UTC) - Expand
gds
Jan. 14th, 2015 06:18 am (UTC)
диагноз: невнятная хуита.
kodt_rsdn
Jan. 14th, 2015 07:37 am (UTC)

Йес ыт ыз!
За углоскобки отдельное фи. На ровном месте усложнили жизнь парсеру.

(no subject) - v_l_a_d - Jan. 14th, 2015 08:19 am (UTC) - Expand
(no subject) - kodt_rsdn - Jan. 14th, 2015 08:39 am (UTC) - Expand
(no subject) - v_l_a_d - Jan. 14th, 2015 09:33 am (UTC) - Expand
(no subject) - thedeemon - Jan. 14th, 2015 08:28 am (UTC) - Expand
(no subject) - kodt_rsdn - Jan. 14th, 2015 08:35 am (UTC) - Expand
(no subject) - thedeemon - Jan. 14th, 2015 09:09 am (UTC) - Expand
(no subject) - kodt_rsdn - Jan. 14th, 2015 09:30 am (UTC) - Expand
(no subject) - dmytrish - Jan. 14th, 2015 11:10 am (UTC) - Expand
zxc_by
Jan. 14th, 2015 09:32 am (UTC)
а как насчёт переписать ещё на ним? (http://nim-lang.org/)
thedeemon
Jan. 14th, 2015 02:07 pm (UTC)
Можете попробовать, будет интересно сравнить.
Nim - тоже довольно занятный экземпляр, делающий успехи сейчас. Но я пока для него не созрел, чем-то он мне противен.
wizzard0
Jan. 14th, 2015 01:25 pm (UTC)
Кажется, что тут нужен gradual typing. Типа, делаем какой-то best effort inference, а где компилятор не угадал - там ему хинтим, что, дескать, делать так-то и так-то.
swizard
Jan. 14th, 2015 05:32 pm (UTC)
Оказывается, когда мы структуру передали в ту функцию, мы ее отдали насовсем, это был move. А вот тупл скопировался, передача по значению, оригинал остается у вызывающей ф-ии. Неожиданно.

Ещё буквально пару недель назад это было не так: структуры тоже обладали Copy, если нет деструктора, и все её члены Copy =) Теперь нужно либо явно указывать #[derive(Copy)], либо #[allow(missing_copy_implementations)] (иначе компилятор нервничает).
thedeemon
Jan. 15th, 2015 01:21 am (UTC)
Фигасе. Я так понимаю, типичный растовец утром встает и переписывает весь свой код, т.к. за ночь произошли фундаментальные изменения в языке. :)
(no subject) - fi_mihej - Jan. 15th, 2015 02:22 am (UTC) - Expand
(no subject) - thedeemon - Jan. 15th, 2015 03:08 am (UTC) - Expand
(no subject) - swizard - Jan. 15th, 2015 05:50 am (UTC) - Expand
(no subject) - thedeemon - Jan. 15th, 2015 06:13 am (UTC) - Expand
(no subject) - alex_akts - Jan. 17th, 2015 07:48 pm (UTC) - Expand
(Deleted comment)
kodt_rsdn
Jan. 17th, 2015 10:12 pm (UTC)
В С++ очень консервативный подход.
Если в первом стандарте (1998) была семантика значений, а следовательно, копирования, то и во всех остальных она останется при том же самом синтаксисе.
А не так, что вышел 2011 стандарт, и опа, весь код в мире резко сломался.

Далее. Семантика значений очень хорошо дружит со временем жизни. Каждая копия валидна до момента формального разрушения.
А семантика перемещения контрынтуитивна. (Как контрынтуитивно написание этого слова :)) )
{ // вход в блок
  Foo x(123), y(456); // создали
  bar(x); // после этой точки x полуживое?!
  buz(x); // сюда мы подсунем какой-то дефолтный мусор?!
  x = y;  // после этой точки x нормальное, y полуживое?
  buz(y); // сюда снова подсунем мусор?
} // выход из блока, окончательное разрушение объектов x, y

Единственный тип, который из коробки обладал таким свойством, это был auto_ptr. И всем программистам кричали в ухо: "берегись автопоинтера".

То, что компилятор Rust умеет ограничивать область видимости до точки перемещения - это он, конечно, молодец!

С другой стороны, перемещение отличается тем, что деструктор/конструктор не вызывается на каждый чих. Полудохлый объект можно повторно использовать, - например, присваивать содержимое, экономя на развёртывании инфраструктуры этого объекта.
std::string x;
for(int i.....)
{
  std::string y (foo(i)); // каждый раз новый буфер
  x = foo(i); // буфер размещён один раз
  .....
}

Так что принудительно сужать область видимости - это платить ненужные деньги.

Да, и ещё момент. В С++, глядя на код, почти всегда видно, где заканчивается время жизни переменной. (Исключение - ссылки на временные объекты).
А в расте, я так понимаю, - фиг. Надо дополнительно лезть читать сигнатуру каждой функции, куда передаётся эта переменная.
То ли дело move. Написал - и сам себе дал знать, что далее там мусор, как если бы присвоил этой переменной белиберду.

Единственно, что синтаксис громоздкий: std::move(var), можно было б какой-нибудь унарный оператор для этого припахать, - да тот же (&&var).
Но это уже консервативность языка.
(Deleted comment)
(no subject) - udpn - Oct. 1st, 2015 06:35 pm (UTC) - Expand
(Deleted comment)
(no subject) - udpn - Oct. 4th, 2015 01:26 pm (UTC) - Expand
(no subject) - thedeemon - Oct. 4th, 2015 04:04 pm (UTC) - Expand
(no subject) - udpn - Oct. 1st, 2015 06:29 pm (UTC) - Expand
(no subject) - thedeemon - Jan. 18th, 2015 06:16 am (UTC) - Expand
(Deleted comment)
thedeemon
Jan. 18th, 2015 06:19 am (UTC)
Я понимаю. Но когда я передаю указатель в функцию, из ее типа компилятор уже знает, что ссылка должна быть мутабельная, писать руками mut можно лишь для пущей вящести, самому компилятору это не нужно.
(Deleted comment)
(Deleted comment)
thedeemon
Jan. 18th, 2015 06:22 am (UTC)
> и это foo(a) должно вычислиться до вычисления второго аргумента

Вот именно, что до. Не одновременно. Порядок вычисления аргументов в расте определен или нет?

>В общем, неправильно ты Rust ругаешь.

Я лишь рефлексирую над тем, чем столкнулся, когда писал эти 50 строчек кода. До более продвинутых вещей еще сам на опыте не добрался, а ругать за теорию не хочу.
(Deleted comment)
(no subject) - thedeemon - Jan. 18th, 2015 12:48 pm (UTC) - Expand
(Deleted comment)
( 51 comments — Leave a comment )