Comments 56
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"
);
А вообще, я тоже сначала в 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.
Как, опять новый веб-движок?
Ооооох.
Немного о движке:
* Architecting Servo: Pipelines and Parallelism
* Layout in Servo: Parallel and Rustic Tree Traversals
Судя по статье и документации, 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. О них я особо ничего не скажу. Но и что-то связанное с этим всем вроде бы снова появляется. Просто по той причине, что все эти штуки имеют внутри много общих черт. Например где-то внутри там планировщик сидит и так далее.
>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
do spawn { // (3)
println((«Message form task 3»);
}
2 открывающие и одна закрывающая. Опечатка или синтаксис?
Сейчас язык потихоньку устаканивается, по крайней мере, с внешней стороны. Кстати, вот буквально в июне его перевели на новый рантайм, написанный на самом расте. Это показатель того, что язык довольно-таки пригоден для системных приложений.
Но, как правильно было сказано, он сейчас довольно трудно юзабельный на нетривиальных вещах. И дело тут даже не в «немного» непривычной семантике (из наличия unique-типов следует очень много весёлых вещей, вроде явных lifetime'ов у borrowed pointer'ов и средств работы с ними), а в общей сырости компилятора. При использовании каких-нибудь сложных штук есть очень большой риск получить ICE, Internal Compiler Error. Но над этим работают очень активно, и баги на багтрекере принимаются от всех желающих.
Язык действительно стремительно улучшается и становится стабильней, но в том состоянии, в котором он сейчас на нём писать проекты, кроме как для того, чтобы оценить язык не стоит. Синтаксис от версии к версии меняется очень сильно. То, что собиралось старой версией не будет собираться новой(кроме тривиальных программ). Сама модель выполнения, когда потоки «не настоящие» требует особой аккуратности от компилятора, ошибка доступа к памяти приведёт к смерти всего процесса со всеми такими потоками.
Приведу пример: следующий код(чуть сложнее чем сишный 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'е.
1) Мне, как питонисту, нравится в go возможность не писать ";" в конце каждой строки. В rust'е правила использования ";" довольно непривычные, от пропущенного ";" часто может измениться семантика, т.е. код будет компилироваться, но делать не то, что от него ожидали.
2) От символов "::" в rust'е у меня немного рябит в глазах, точка нравится больше.
3) В rust'е много внимания уделено менеджменту памяти и потокобезопасности, поэтому поначалу путаешься какой тип указателей использовать(а их четыре: @, ~, & и *).
4) В rust'овский match влюбился сразу же! На мой взгляд это самая вкусная особенность языка.
Но, на самом деле, это все дело вкуса и привычки.
Inherent mutability — тоже очень интересная фича, я ни в одном другом языке такого не видел. Сначала может показаться, что с этим непонятно как жить, но гибкий механизм impl'ов здесь спасает.
Но вот что мне больше всего не нравится в Go — это отсутствие любого рода дженериков. Это очень серьёзное упущение для современного языка. В Rust дженерики есть, и достаточно мощные.
А вот то, что методы
Push()
и Pop()
тамошнего интерфейса Interface
используют interface{}
в качестве типа для данных — это очень печально и как раз есть то, с чем бы дженерики очень помогли. В некотором роде, их отсутствие даже странно, потому что Go позиционируется как язык с довольно строгой системой типов (отсутствие неявных преобразований тому подтверждение). И то, что для написания достаточно обобщённого кода приходится обращаться к динамическим кастам, сильно разочаровывает.А параметрический полиморфизм — это и не есть фича функциональных языков)
Все же в рускоязычной литературе managed обычно переводится как «Управляемый». На примере того же .Net — managed language — управляемый язык, managed code — управляемый код. По аналогии unmanaged — неуправляемый.
отсутствие 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 тут самое место.
Хотя возникает вопрос — зачем? Ты же понимаешь, что такой код должен быть помечен как unsafe и проверка работы с памятью времени компиляции к нему применяться не будет.
Я про другое. Вот этот код на другом языке, на Erlang, Limbo или вообще каким–нибудь Intel Cilk должен иметь возможность сделать yield, создавать зелёные потоки, делать обмен сообщений между этими потоками.
До тех пор, пока нет общей платформы, каждый тянет в свою сторону, как в басне Крылова. Intel Cilk, Limbo, Go, Erlang, каждый по отдельности с зелёными потоками, а вместе никак.
Ключевые возможности Rust