Pull to refresh

Comments 56

Так написано, что не стал дочитывать. Проверяйте тексты перед публикацией.
Так — как? «Так написано» — это ну просто очень-очень информативно!
Зря минусуете. Первый абзац со списком и правда были очень удручающими. Спасибо, что поправили.
О чем можно просто написать в личку, так как не велика трагедия, верно? Как, впрочем, несколько человек и сделало.
Написано отлично и развернуто. Пользуясь случаем благодарю за популяризацию.
А есть ли в языке поддержка интринсиков для работы с SIMD операциями или возможность написания инлайн ассемблерного кода?
Синтаксис не очень, конечно…
Это почему? Там простой ассемблерный код в AT&T стиле и пока нет возможности использования памяти «из программы», вроде этого:
float a[4] = {1.2f, 2.0f, 3.0f, 4.0f}, b[4];

__asm__ (
	"movaps %[a], %%xmm0\n\t"
	"mulps %xmm0, %xmm0\n\t"
	"movaps %xmm0, %[b]\n\t"
	:
	: [a]"m"(*a), [b]"m"(*b)
	: "%xmm0"
);
AT&T синтаксис — не самое лучшее, что я видел, на самом деле. Пишут что есть возможность использовать интеловский, и то хорошо.
Чем вам AT&T не угодил? Лично мне, AT&T больше нравится.
А вообще, я тоже сначала в NASM/YASM на Intel-синтаксисе писал. Когда на GAS переходил, изучил AT&T, где единственная проблема была, это операнды в другом порядке (что быстро ушло, когда я «набил руку»). Плюс, AT&T более «явный» (это я к суффиксам b/w и т.д.).
Да и потом, лучше AT&T изучать и применять, т.к. он еще и на других ассемблерах используется (arm, sparc, ppc и т.д.).
P.S. А если некто на ассемблере вообще никогда не писал, так ему совершенно все равно будет.
Лучше, строго говоря, применять интринсики.
AT&T слишком «другой» по сравнению с MSVC-шным синтаксисом, который был у меня первым, и который я до сих пор считаю лучшим, плюс как уже сказали, непохожесть на nasm/yasm, только и всего. Плюc AT&T на первый взгляд содержит немного больше мусора, чем остальные варианты.
Не знаю, почему они решили использовать его дефолтным, разве что для совместимости с gcc/clang.
Делать ассемблерные вставки однозначно можно. Но, вряд ли его использование в рамках Rust можно считать хорошим решением, т.к. компилятор не сможет провести анализа работы с памятью и одна из ключевых возможностей — гарантия безопасности операций, на данный код распространяться не будет.
Только если там не будет возможности использования SSE/AVX, то придется только так. Например в Go пакета с SSE/AVX нет, поэтому приходится import «C» использовать и расширения gcc (или тот-же asm()).
> Основным продуктом, разрабатываемым на Rust, является новый веб-движок Servo
Как, опять новый веб-движок?
Ооооох.
Скоро ждать аналога от гугла и очередного витка войны браузеров?
Да они и так конкурируют. КМК, Мозилла решила, что имея в своем распоряжении движок написанный на языке типа Rust, у них будет конкурентное преимущество над разработчиками с C++ движками. Подобное ожидание выглядит довольно разумно, т.к. многопоточный код на Rust писать быстрее и проще чем на C++. А если еще и производительность будет не больше чем 1,5-2 раза ниже чем у плюсового кода, то это вообще победа :)
Только вот они бы для начала спецификацию продумали до конца и что туда включать. А то это добавят, это добавят, это поломают, там починят. Это все-таки не программа, а язык программирования, если делать с ним что ни попадя, то результат будет плачевным (костыль на костыле и т.п.).
Для версии 0.7 — это нормальная ситуация. Ближе к 1.0 язык стабилизируют.
В этом состоит прекрасная часть Rust: этот язык community driven. Пока не заморожена 1.0 вы можете написать свой фидбек на синтаксис или фичи и если этот фидбек конструктивен — к нему прислушаются.
Не доверяю я этим вашим «комьюнити», там много людей разной компетенции, много идей и пихать все это в кучу плохая мысль. Я больше доверяю лицам вроде создателей Go, которые, как выразился Бобок на Radio T, на языках собаку съели (а еще на OS).
Ну, Rust тоже не первоклассники пишут, я ж не говорю что каждый может туда всё что хочешь впихнуть. Но способ разработки, когда с одной стороны не заморожен API, а с другой язык щупают на практике и получают фидбек, согласитесь, имеет смысл. И вот тут помощь коммьюнити бесценна.
Весьма хорошо, что разработчики постепенно понимают важность удобного написания и исполнения параллельного кода и корутин, и выходят новые удобные инструменты для этого. Конкуренция в таких вещах положительно влияет на удобство и возможности (<joke>и демонстрирует фанатам node.js его ущербность</joke>)

Судя по статье и документации, Rust — это чуть более человечный Erlang. Те же immutable структуры, попытки минимизировать использование shared state посредством мучений разработчиков при их использовании, и всякие приятные вещи типа pattern matching'а.

С другой стороны, есть Go. Помимо его очевидного недостатка (Not invented here), и перекладывания на разработчика ответственности за целостность shared state, он даёт гораздо больше возможностей для работы, чтобы называть его языком более высокого уровня, чем Rust, при сохранении удобства и эффективности параллельного исполнения кода и использования памяти (ну и, само собой, не мучает разработчика immutable переменными, отсутствием необходимых структур типа ассоциативных массивов, и прочими ограничениями).

Посмотрим, что из этого выйдет.

Ну как, и какие впечатления от того, что вышло? Сравнение с го в 2013 прям как в воду :)

Корутины вырезали, но вроде бы пытаются вернуть.

Да нет, не собираются. Есть токио и человеческие асинк-авейты, зачем корутины?

Затем, что это и есть корутины. Один из подвидов, а именно stack-less coroutines, которые ещё можно назвать как generators. https://doc.rust-lang.org/nightly/std/ops/trait.Generator.html. Поверх этих штук async/await и пилится.


Ещё есть stack-full coroutines, которые green-threads. Они были когда-то давно в каком-то виде и их вырезали к версии 1.0. О них я особо ничего не скажу. Но и что-то связанное с этим всем вроде бы снова появляется. Просто по той причине, что все эти штуки имеют внутри много общих черт. Например где-то внутри там планировщик сидит и так далее.

Наконец то нашел подробный материал по Rust, спасибо вам. После прочтения сложилось впечатление, что идеологии Rust и D очень схожи: Erlang-like многопоточность, строгий следящий за shared состояниями компилятор, разделение кода на unsafe и safe.
Да не за что. В принципе, я свои ощущения и небольшие заметки еще сюда складываю, можно и там глянуть, если интересно.
Самый интересный нативный язык на данный момент. Спасибо за статью.
static.rust-lang.org/doc/tutorial.html#generics
>Inside a generic function, the names of the type parameters (capitalized by convention) stand for opaque types. All you can do with instances of these types is pass them around: you can't apply any operations to them or pattern-match on them.
Ололо, type-erasure
Ну, на самом деле именно из этого куска наличие стирания типов не следует (где-нибудь в скале без дополнительных рефлективных действий тоже ничего не сделаешь с параметром типа T), но по факту да, в Rust присутствует стирание типов. Они ближе к шаблонам, чем к дженерикам.
do spawn { // (3)
println((«Message form task 3»);
}

2 открывающие и одна закрывающая. Опечатка или синтаксис?
Вообще раст — очень годный язык. Я следил за ним почти с самого его появления, и с тех пор он очень сильно поменялся, причём в лучшую сторону. Многие лишние фичи выкинули, новые более удобные добавили, что-то привели в гораздо более приятную форму.

Сейчас язык потихоньку устаканивается, по крайней мере, с внешней стороны. Кстати, вот буквально в июне его перевели на новый рантайм, написанный на самом расте. Это показатель того, что язык довольно-таки пригоден для системных приложений.

Но, как правильно было сказано, он сейчас довольно трудно юзабельный на нетривиальных вещах. И дело тут даже не в «немного» непривычной семантике (из наличия unique-типов следует очень много весёлых вещей, вроде явных lifetime'ов у borrowed pointer'ов и средств работы с ними), а в общей сырости компилятора. При использовании каких-нибудь сложных штук есть очень большой риск получить ICE, Internal Compiler Error. Но над этим работают очень активно, и баги на багтрекере принимаются от всех желающих.
Работал с растом с версии 0.5. Для маленьких проектов, вроде, всё стабильно, а, когда объём кода становится большим, то начинают отовсюду лезть трудноотлавливаемые ошибки.

Язык действительно стремительно улучшается и становится стабильней, но в том состоянии, в котором он сейчас на нём писать проекты, кроме как для того, чтобы оценить язык не стоит. Синтаксис от версии к версии меняется очень сильно. То, что собиралось старой версией не будет собираться новой(кроме тривиальных программ). Сама модель выполнения, когда потоки «не настоящие» требует особой аккуратности от компилятора, ошибка доступа к памяти приведёт к смерти всего процесса со всеми такими потоками.

Приведу пример: следующий код(чуть сложнее чем сишный switch-case) вызывает segmentation fault даже в последней стабильной версии раста:

код
enum Command {
    Create{qname:~str},
    Help,
    Unknown,
}

fn main() {
    let val = Unknown;

    match val{
        Create{qname: qname} => {
            print(fmt!("Create %?", qname));
        }
        Help => {
            print(~"Help");
        }
        _ => {
            print(~"Other");
        }
    }
}


При этом не важно, сколько растовских тасков было запущено все они умрут!

Вот ещё несколько ссылок на баги, встреченных и зарепорченных мной: 5999, 5998, 5906.

В противоположность к этому, компилятор языка go, даже с ранних версий, генерировал очень стабильный код(или мне везло). Хоть и писал на нём больше чем на rust'е.
Как вам Rust против Go в плане синтаксиса?
Их сложно сравнивать, потому что они разные:
1) Мне, как питонисту, нравится в go возможность не писать ";" в конце каждой строки. В rust'е правила использования ";" довольно непривычные, от пропущенного ";" часто может измениться семантика, т.е. код будет компилироваться, но делать не то, что от него ожидали.
2) От символов "::" в rust'е у меня немного рябит в глазах, точка нравится больше.
3) В rust'е много внимания уделено менеджменту памяти и потокобезопасности, поэтому поначалу путаешься какой тип указателей использовать(а их четыре: @, ~, & и *).
4) В rust'овский match влюбился сразу же! На мой взгляд это самая вкусная особенность языка.

Но, на самом деле, это все дело вкуса и привычки.
У Go меньше возможностей в языке заложено, он проще. Про стабильность уже говорили, в Go код генерируется достаточно качественный. С другой стороны, код на Rust поддерживает за счёт системы типов и указателей больше инвариантов => больше безопасность, особенно в случае многопоточности. Плюс в Rust очень удобные ADT и паттерн-матчинг на них.

Inherent mutability — тоже очень интересная фича, я ни в одном другом языке такого не видел. Сначала может показаться, что с этим непонятно как жить, но гибкий механизм impl'ов здесь спасает.

Но вот что мне больше всего не нравится в Go — это отсутствие любого рода дженериков. Это очень серьёзное упущение для современного языка. В Rust дженерики есть, и достаточно мощные.
В Go работа с интерфейсами весьма близка к generic'ам, и их мощность хорошо видна даже по стандартной библиотеке, где, например, везде где можно используются io.Reader'ы и io.Writter'ы
Нет. Дженерики — это параметрический полиморфизм, а интерфейсы Go — это ближе к subtyping-полиморфизму. Они решают разные задачи.
Безусловно, Go не функциональный язык, и всего богатства параметрического полиморфизма там нет. Однако, если интерфейсы Go сравнивать с generic'ами в каком-нибудь C# или Java, то значительное количество кейсов использования generic'ов последних легко реализуется в рамках Go, что хорошо видно, например, по реализации кучи в стандартной библиотеке Go.
Посмотрел на пакет container/heap. Очень интересный подход, практически не свойственный другим языкам, однако дженерики там не причём. Он вполне реализуется на интерфейсах той же джавы, пусть и с некоторыми костылями, связанными с отсутствием в Java решения expression problem.

А вот то, что методы Push() и Pop() тамошнего интерфейса Interface используют interface{} в качестве типа для данных — это очень печально и как раз есть то, с чем бы дженерики очень помогли. В некотором роде, их отсутствие даже странно, потому что Go позиционируется как язык с довольно строгой системой типов (отсутствие неявных преобразований тому подтверждение). И то, что для написания достаточно обобщённого кода приходится обращаться к динамическим кастам, сильно разочаровывает.

А параметрический полиморфизм — это и не есть фича функциональных языков)
«Разделяемые указатели (Managed boxes)»

Все же в рускоязычной литературе managed обычно переводится как «Управляемый». На примере того же .Net — managed language — управляемый язык, managed code — управляемый код. По аналогии unmanaged — неуправляемый.
Да, в рускоязычной литературе managed обычно переводится как «Управляемый». В то же время, в Rust есть несколько типов управляемых указателей, поэтому переводить какой-то один из них как «управляемы» было бы не совсем корректно. К тому же, при подборе русского названия я еще исходил из поведения указателя.
SMP — архитектура компьютеров. Как следствие, вопрос не совсем понятен.
Испрользовал терминологию из Erlang… Имею в виду, умеют ли зеленые процессы расходиться по ядрам процессора
Да, такая фича там была с самого начала. Есть возможность использовать разные планировщики с разными принципами раздачи задач по ядрам (на данный момент, версия 0.8-pre, вроде как остался только один, т.к. заменили ядро планировщика).
отсутствие null-указателей

Реально? Но ведь они же постоянно нужны. Например, можно было бы намекать сборщику мусора, что объект нужно удалить, путём присвоения указателю нулевого указателя.

Для создания разделяемых указателей используется унарный оператор @

Как я понял, код
let x = @Point {x: 1f, y: 1f};

не просто создаёт разделяемый указатель, как вы написали, он создаёт сам объект Point в куче и кладёт указатель на него в x. Т. е. не просто берётся адрес от объекта типа Point, а для него выделяется память. Т. е. этот код ближе к такому коду на C++:
Point *x = new Point(1.0, 1.0);

чем к такому (сломанному):
Point *x = &Point(1.0, 1.0);

Т. е., как я понял, @ ведёт себя как new. Тогда про это нужно написать в статье. То же с ~.

Ещё у меня такой вопрос: @ (и ~) всегда ведёт себя как new? Или всё-таки есть ситуация, когда @ просто берёт адрес? Например, что будет в следующем коде?
let x = @0;
let y = @*x;

@ просто возмёт адрес и таким образом в y будет лежать тот же указатель, что и в x? Или @ всё-таки выделит новую память и объект скопируется?
Реально? Но ведь они же постоянно нужны. Например, можно было бы намекать сборщику мусора, что объект нужно удалить, путём присвоения указателю нулевого указателя.


Нулевой указатель вполне заменяется использованием более безопасных конструкций: Выражениями, возвращающими значение, вместо процедурного стиля (см. if, match, etc.), Option/Maybe для возможно отсутствующего значения.

С GC же все проще: зачем занулять ссылку, если скоро функция кончится и ссылка сама уйдет со стека? Если же это бесконечный цикл или поле долгоживущего объекта, то Option/Maybe тут самое место.
А как там обстоят дела с разноязыковым взаимодействием? На планировщике Rust можно запускать кусочки кода на другом языке, хотя бы теоретически, чтоб они вперемешку с блоками Rust работали?
Планировщик Rust «выполняет» обычный машинный код. По умолчанию Rust поддерживает вызовы Си кода. Ну а через Си ты уже можешь вызвать что угодно.
Хотя возникает вопрос — зачем? Ты же понимаешь, что такой код должен быть помечен как unsafe и проверка работы с памятью времени компиляции к нему применяться не будет.
Будет, но другим компилятором.

Я про другое. Вот этот код на другом языке, на Erlang, Limbo или вообще каким–нибудь Intel Cilk должен иметь возможность сделать yield, создавать зелёные потоки, делать обмен сообщений между этими потоками.

До тех пор, пока нет общей платформы, каждый тянет в свою сторону, как в басне Крылова. Intel Cilk, Limbo, Go, Erlang, каждый по отдельности с зелёными потоками, а вместе никак.
Sign up to leave a comment.

Articles