Comments 172
Как же удалось отказаться от сборщика мусора?
А вот так! Память в компиляторе начали представлять как ресурс, «владеть» («ownership») которым может одна и только одна переменная.
RAII в C++ (к примеру std::unique_ptr)?
Причина повышения надёжности в том, что в Rust всё подчиняется логике владения и заимствований, за исключением нескольких операций доступных только в unsafe
блоках.
{
Foo* a = new Foo{};
std::unique_ptr<Foo> p1{a};
std::unique_ptr<Foo> p2{a};
// Упс, двойное освобождение памяти в конце блока
}
В расте это ошибка компиляции.
Случайно, на стыке старого и нового кода. Или просто по незнанию...
Вот, всего месяц назад на ruSO пришлось по-воевать в комментариях за удаление ответа с таким вот кодом:
void do_something(shared_ptr<Base> base)
{
shared_ptr<Derived> derived( dynamic_cast<Derived*>(base.get()));
}
Вроде 9000 репутации у отвечавшего, а проблему в этом коде он не видел.
std::unique_ptr<int> p = std::make_unique<int>(92);
auto fn_once = [p = std::move(p)]() mutable {
std::cout << *p << std::endl;
p.release();
};
fn_once();
fn_once();
К сожалению, глядя на код вы не можете сказать, просто так он вызван или не просто так. Особенно когда все такие умные, что считают ниже своего достоинства написать комментарий, зачем это здесь.
Все ручное управление типа get() release() если по честному то приравнивается к ансейф расту, т.к. эти методы нету смысла вызывать в с++ коде. А связь с си в расте тоже ансейф. Другое дело что вы можете сказать что в расте ансейф более явный. Но вообще юзе афтер мув более менее отлавливают анализаторы. Но неприятности как раз приходят в менее тривиальных случаях.
std::unique_ptr
может как указывать на объект, так и быть пустым. В Rust std::boxed::Box
всегда что-то содержит.
Примерно, только за RAII в плюсах нужно следить самому, а тут у вас для этого есть компилятор. Хороший комментарий в тему: https://habr.com/ru/post/415737/#comment_18835083
Да, он еще молод (2 года как стабилизировался)
2 года? Вообще-то расту уже скоро 5 будет, 1 версия вышла в мае 2015. В 2018 создали уже вторую версию, т.н. Rust 2018 Edition, которая фиксит многие огрехи первой версии.
Единственные утешения, идущее от Golang:
встроенные в язык «green threads» (аля корутины), но они есть уже даже в python
компиляция в нативный код, только непонятно, зачем (возможно, чтобы Docker работал) :-)
И совсем мутная ситуация с пакетным менеджером. Даже в Node.js и python он есть.
Где мутность? Так же сто лет уже есть пакетный менеджер и он так же развивается, как развивается NPM или Cargo.
встроенные в язык «green threads» (аля корутины), но они есть уже даже в python
Треды суперлегкие. + Приложения с одной и та же логикой реализованные к примеру на node.js, python от 100 до 10 раз потребляют больше ресурсов (оперативная память, утилизация процессора) чем программа на Go
Всё ещё не понятно, как этот пакетный менеджер работает.
Предполагалось изначально централизованное хранилище пакетов для всех приложений на машине. Да с версиями они изначально «протупили», но уже как год, полтора в механизм управления зависимостями включено версионировании и возможность хранения зависимостей непосредственно локально с приложением.
Видно с введением go modules
Есть актикс, ну и в общем-то всё. Остальные фреймворки далеко не оптимальны и даже на каком-нибудь го можно получить результат быстрее и лучше.
Основная проблема — веб очень сильно базируется на асинхронности, с которой в языке до сих пор всё ужасно. Для стороннего наблюдателя не особо видно, но писать асинхронный код на расте до сих пор очень больно. Думаю это будет продолжаться еще как минимум полгода. Когда асинк/авейт наконец попадет в стейбл, когда библиотеки стабилизируют асинхронное апи, когда токио выползет на версию 1.0, вот тогда что-то можно будет делать.
На расте уже сейчас можно написать самый быстрый веб-сервер из всех языков, тот же бенчмарк веб-фреймворков не даст соврать. Но в процессе этого вы накушаетесь проблем по самое не балуй. На том же го вы получите 30% результата за 10% времени или около того.
Когда асинк/авейт наконец попадет в стейбл
Уже же.
Емнип он пока в бете. В стебл в следующий релиз.
Но даже без этого, последние батлы async-std против токио, tokio 0.1 vs 0.3, compat::Backward/Forward туда-сюда, невнятные стримы, апишки которые появляются и исчезают с разницей в пару недель, и дока которая при этом отстает на месяцы (и поэтому неюзабельна полностью).
В общем, пока всё плохо.
0.2 tokio уже релизнули, остальные библиотеки тоже стабилизируются на 0.2 версии уже.
0.2 токио вообще по сути был плейграундом для третьей. Не помню ни одной серьезной либы которая поддерживала бы его не в рамках эксперимента. 0.1 и 0.3, причем 0.3 до сих пор сырая.
В общем, я бы хотел сказать что у раста всё ок, да не могу. Я вот в последнй статье сравнивал хаскель и го, попробовал на расте напилить то же самое, и минут 20 искал как бы мне заавейтить стрим и получить вектор байт. Так и не нашел, потому что дока предлагала методы которых на самом деле у типов не было.
0.2 токио вообще по сути был плейграундом для третьей. Не помню ни одной серьезной либы которая поддерживала бы его не в рамках эксперимента. 0.1 и 0.3, причем 0.3 до сих пор сырая.tokio 0.2 зарелизилась только совсем недавно. Несколько библиотек в альфе (hyper тот же если не ошибаюсь) таргетились на альфу tokio. Вы с futures крейтом не перепутали? Это именно там были эти backwards/compat.
заавейтить стрим и получить вектор байт
Ну вот как раз в tokio который поддерживает async/await пример:
github.com/tokio-rs/tokio/blob/master/examples/print_each_packet.rs#L89-L102
До этого тоже самое но на комбинаторах делалось.
Так и не нашел, потому что дока предлагала методы которых на самом деле у типов не было.А версии зависимости у вас совпадали? в некоторых крейтах надо явно активировать features чтобы больше было доступно.
P.S. Ситуация с асинхронщиной в расте еще совсем недавно была совсем печальная, но сейчас с async/await стабилизацией все очень бурно становится адекватным (сам слежу за асинхронщиной в расте года так с 2016-го).
А так да, большую часть времени вкатиться в асинхронщину на расте было очень сложно (но поняв базовые концепты все становилось просто). Сейчас с async/await очень скоро все станет гораздо лучше (собственно, уже в какой то мере).
Ну вот как раз в tokio который поддерживает async/await пример:
github.com/tokio-rs/tokio/blob/master/examples/print_each_packet.rs#L89-L102
До этого тоже самое но на комбинаторах делалось.
Да нет, мне надо было не сервер написать, а клиент. Я взял гипер, попробовал на нем написать и… И все.
После того как я пришел к тому, что в футурами миллион ошибок в стиле
error[E0733]: recursion in an `async fn` requires boxing
--> src\main.rs:24:43
|
24 | async fn get_comments(node: Tree<i32>) -> Tree<Comment> {
| ^^^^^^^^^^^^^ recursive `async fn`
|
= note: a recursive `async fn` must be rewritten to return a boxed `dyn Future`.
Которые решаются костыликами вроде:
fn get_comments(node: Tree<i32>) -> Pin<Box<dyn Future<Output = Tree<Comment>>>> {
Box::pin(async move {
match node {
Tree::Leaf(x) => Tree::Leaf(get_comment(x).await.unwrap()),
Tree::Branch(x, left, right) => {
let result = get_comment(x).await.unwrap();
let left = get_comments(*left).await;
let right = get_comments(*right).await;
Tree::Branch(result, Box::new(left), Box::new(right))
}
}
})
}
То я забил.
В итоге так и не понял как жсон получить на hyper = "0.13.0-alpha.2". concat2 нет, TryStreamExt не компилится, Into есть в доке, но в альфе выпилили
Только я не нашел пример как на нем делать асинк-авейт запросы эффективно. Покажите, пожалуйста, если не трудно.
Когда я его ковырял он поддерживал только синхронные методы. Щас вроде что-то повяилась, но документация как всегда: https://docs.rs/reqwest/0.8.4/reqwest/unstable/async/index.html
Даже когда идея показывает
= note: expected type ()
found type futures::future::and_then::AndThen<futures::stream::concat::Concat2<hyper::body::body::Body>, futures::future::or_else::OrElse<futures::future::map::Map<futures::future::and_then::AndThen<futures::future::and_then::AndThen<futures::future::map_err::MapErr<futures::future::result_::FutureResult<contract::Update, serde_json::error::Error>, [closure@src\main.rs:139:22: 144:14]>, std::result::Result<(contract::User, std::string::String, i64, i64), http::response::Response<hyper::body::body::Body>>, [closure@src\main.rs:145:23: 162:14]>, futures::future::map_err::MapErr<futures::future::and_then::AndThen<impl futures::future::Future, futures::future::either::Either<futures::future::and_then::AndThen<impl futures::future::Future, futures::future::either::Either<futures::future::then::Then<impl futures::future::Future, futures::future::either::Either<impl futures::future::Future, futures::future::result_::FutureResult<(), telegram_client::TelegramClientError>>, [closure@src\main.rs:211:51: 224:46 telegram_client:_, chat_id:_, text:_]>, futures::future::result_::FutureResult<(), telegram_client::TelegramClientError>>, [closure@src\main.rs:173:90: 230:30 file_id:_, ext:_, user:_, message_id:_, dbs:_, chat_id:_, telegram_client:_]>, futures::future::result_::FutureResult<(), telegram_client::TelegramClientError>>, [closure@src\main.rs:166:31: 235:22 user:_, chat_id:_, message_id:_, telegram_client:_, file_id:_, dbs:_]>, [closure@src\main.rs:236:30: 242:22]>, [closure@src\main.rs:163:23: 243:14 telegram_client:_, dbs:_]>, [closure@src\main.rs:245:18: 248:14]>, std::result::Result<http::response::Response<hyper::body::body::Body>, hyper::error::Error>, fn(http::response::Response<hyper::body::body::Body>) -> std::result::Result<http::response::Response<hyper::body::body::Body>, hyper::error::Error> {std::result::Result<http::response::Response<hyper::body::body::Body>, hyper::error::Error>::Ok}>, [closure@src\main.rs:136:54: 250:6 telegram_client:_, dbs:_]>
Сильно удобнее не становится.
Haskell откровенно не взлетел по причине агрессивных «понтов» — чтобы хорошо понять его терминологию, нужно быть, как минимум, кандидатом наук со специализацией в области одной из лидирующих теорий в современной математике: теории категорий.
Это даже не преувеличение, это откровенная ложь. Работаю с «продвинутым» хаскелем и не испытываю трудностей, хотя сам не имею ни высшего образования, ни математического бэкграунда.
А что такого сложного в программистком понимании апликативного функтора?
Кроме того интересно, где это берут на работу хаскелистов, которые говорят о неимоверной сложности апликативного функтора?
"аппликативный функтор" вещь намного проще, чем например "паттерн мост".
== Rust и почему от такой популярный и востребованный.
зачем здесь эта ссылка, и откуда инфа про популярность и востребованность Rust? Выдаёте свои фантазии за реальность и констатируете их как нечто самоочевидное.
== встроенные в язык «green threads» (аля корутины), но они есть уже даже в python
«green threads» в Go ничего общего не имеют с корутинами питона. Вы бы поизучали вопрос прежде, чем делать высказывания космического масштаба и космической же глупости.
== компиляция в нативный код, только непонятно, зачем (возможно, чтобы Docker работал)
Похоже, у вас проф. левель недостаточно высок для понимания. Спросите у тех. кому понятно. Вам объяснят на пальцах.
== совсем мутная ситуация с пакетным менеджером.
Любая ситуация будет мутной, если смотреть на неё через призму предрассудков и нежелания читать документацию.
Но вы пишите ещё, ваше мнение очень важно для всех нас.
«green threads» в Go ничего общего не имеют с корутинами питона. Вы бы поизучали вопрос прежде, чем делать высказывания космического масштаба и космической же глупости.
это моя любимая тема, в которой специализируюсь последние несколько лет, увы
Похоже, у вас проф. левель недостаточно высок для понимания. Спросите у тех. кому понятно. Вам объяснят на пальцах.
я знаю почему — модно :-)
это моя любимая тема, в которой специализируюсь последние несколько лет, увы
Насколько я понимаю, комментатор выше имел в виду что горутины в go реализуют вытесняющую модель многозадачности, а корутины в python — кооперативную.
В горутинах нам не надо явно писать yield и решать, где приостановить одну горутину и возобновить другую — шедулер языка сам этим занимается.
Поэтому это две большие разницы, и говорить, что питоновские корутины это аналог горутин и то что они «есть уже даже в python» — некорректно.
Ведь всё это и в Go и в Python — виртуальная многозадачность в пространстве пользователя
В Go — реальные потоки по числу ядер процессора (можно ограничить в настройках программы и использовать не все ядра, если тебе надо) умножить на «виртуальную» в пределах одного ядра.
То есть если у вас 16 ядер, то на Go вы можете запустить хоть 10 000 потоков. При этом в действительную параллель вытесняющим образом будут выполнятся только 16, что обеспечаны железом, конечно же.
Остальные будут переключаться кооперативным образом с этими 16-ю (например, на функциях синхронизации/ввод-вывода).
Насколько я знаю, в Go многозадачность очень даже кооперативная — просто yield скрыт под капотом и не светится. Т.е. горутины прерываются только на ожидании асинхронных операций типа чтения-записи сокета или ожидания на канале.
Я вообще затрудняюсь сказать кто, кроме эрланга (почему-то вообще не упомянутого в статье, в которой ну очень странно даже про него не обмолвиться) и смолтока, умеет в нормальную вытесняющую многозадачность.
В вытесняющую многозадачность умеет только шедулер ОС, вот только проблема в том, что он медленный. Поэтому и используются "грин-треды", которые так не умеют, зато быстрее.
nathanmlong.com/2017/06/concurrency-vs-paralellism тут вот внятно для людей, которые думают, что грин-треды — это не болото, которое повиснет намертво при первом удобном случае.
Я бы не стал называть подход, когда VM вставляет в код неявные проверки "не пора ли переключиться на планировщик", вытесняющей многозадачностью.
Код, выполняющийся в потоке, сам отдаёт управление планировщику, кооперативно. И если например в этом потоке в данный момент выполняется какой-нибудь код на C, ничего не знающий про BEAM, не будет никакого переключения.
Да, это позволяет снизить latency, но эти проверки не бесплатны. И разработчик не может сказать VM: "вот тут проверяй, а тут и так сойдёт".
думают, что грин-треды — это не болото, которое повиснет намертво при первом удобном случае
Я думаю, что это — инструмент, которым надо уметь пользоваться и знать его ограничения. И кстати то, что сделано в BEAM, тоже можно назвать грин-тредами.
VM вставляет в код неявные проверки «не пора ли переключиться на планировщик», вытесняющей многозадачностью
Так и не называйте, VM не вставляет в код никаких проверок (VM вообще ничего в код вставить не может, компилятор может, но и он этого не делает). VM эмулирует процессор для каждого «процесса» (в кавычках, потому что это не системные процессы) и отдает каждому по несколько сотен/тысяч тиков.
если например в этом потоке в данный момент выполняется какой-нибудь код на C
Угу, половина стандартной библиотеки эрланга — NIFs, и ничего, планируются как миленькие.
разработчик не может сказать VM: «вот тут проверяй [...]»
Опишите ситуацию, когда я хотел бы, чтобы вот тут код намертво заблокировал одно из ядер на пять минут, пока он файл до конца не вычитает.
[green threads] инструмент, которым надо уметь пользоваться [...]
Армстронг рассказывал, как он был на какой-то конференции в начале 90-х и все хвастались производительностью, удобством использования и прочими плюшками своих языков. А он подходил ко всем и спрашивал: «А как насчет fault-tolerance?» Потому что рассчитывать на то, что разработчик никогда не сделает ошибку — по меньшей мере наивно.
Вот хороший бенчмарк для Phoenix (это заслуга OTP, конечно) — два миллиона соединений на 40core/128gb box. Основное отличие — то, что я могу в миллионе из этих соединений считать факториалы больших чисел, и они ответят клиентам, когда будут готовы, а остальной миллион — этого вообще не заметит.
Если это не вытясняющая многозадачность — то я не знаю, что тогда.
VM не вставляет в код никаких проверок
И интерпретатор, и JIT — это часть VM.
отдает каждому по несколько сотен/тысяч тиков
Если это не интерпретатор с циклом (а я думаю, JIT там есть), то нельзя взять и отдать коду N тиков, можно только сделать этот код таким, что он будет сам их считать. И насколько я знаю по беглому изучению этой темы, в BEAM "тиком" считается вызов функции.
Угу, половина стандартной библиотеки эрланга — NIFs, и ничего, планируются как миленькие.
Код на C будет считать "тики" только если делать это явно. Наверняка в стандартной библиотеке так и делается. Возьмите любую левую либу и она вам запросто подвесит всё.
заблокировал одно из ядер на пять минут, пока он файл до конца не вычитает
Ну во-первых, в худшем случае блокируется не ядро CPU, а поток ОС, а во-вторых, освобождать его на время IO как раз таки умеет любой нормальный рантайм с green thread'ами.
То, что BEAM при этом ещё и хорошо справляется с тяжёлыми вычислениями на CPU без ущерба для latency — это несомненно очень хорошая фича. Но и без этого можно жить. Про Go не скажу, но например в Kotlin для этого есть средства. Но да, об этом надо подумать разработчику, и да, рано или поздно он подумать об этом забудет. Но везде можно выстрелить себе в ногу, это лишь одна из возможных ошибок, при том не самая фатальная.
А откуда возьмётся именно дедлок? Закончатся тяжелые задачи, и планировщик получит таки управление.
В основном из-за того, что автор много рассуждает о языках, о которых знает, судя по всему, только понаслышке.
Ну или знает не понаслышке, но очень уж поверхностно.
Кауфман В.Ш. Языки программирования: концепции и принципы
можете привести конкретный пример, попробую ответить конкретно, подискутируем?
Ну вот, например.
(все же знают, что «npm» в Node.js написан на «rust»?)
Насколько я знаю, на расте там только сервис авторизации переписывали.
А вы пробовали дальше заголовка читать?
Не подумайте, что придираюсь, но, честно говоря, формулировка
все же знают, что части «npm» в Node.js написаны на «rust»
не сильно лучше. Потому что эти "части" это авторизация в регистри. Регистри это важный компонент экосистемы, но непосредственно к Node.JS не относится. Ну и кроме того, npm это не только регистри для Node.JS — но ещё и для огромного количества фронтовых пакетов...
Лично мне раст при этом нравится, но как-то чересчур сильно сову на глобус натягиваете — лопнет же. На сайте самого раста, кстати, формулировка абсолютно корректна.
Вместо этого натягивания, вы могли бы, например, написать про Deno. Это уже гораздо интереснее и масштабнее.
Вы знаете, я бы поверил, что кривая вхождения не такая уж сложная, если бы я уже (в латентном режиме с всплесками каждый месяц) уже пол-года не тупил на попытке написать простенький IntoIterable для своей (простой) структуры. Вроде всё понятно, а компилятор не согласен.
А можно пример? Проблемы с итераторами обычно начинаются только при попытке что-нибудь такое запихнуть в трейт (а-ля trait Foo { fn get_iterator(&self) -> impl Iterator<Item=i32>}
).
Мне трудно приводить пример того, от чего у меня мозги скрипят. Вот сейчас попробовал ещё раз написать его. Дано: структура со счётчиком, итерировать от start до end.
struct Test {
start: i32,
end: i32,
counter: i32,
}
impl Test {
fn new(start: i32, end: i32) -> Test {
Test{start:start, end:end, counter:0}
}
}
impl Iterator for Test {
fn next(&mut self) -> Option<i32>{
if self.counter < self.end {
self.counter +=1;
return Some(self.counter-1);
}
else {
return None
}
}
}
impl IntoIterator for Test {
type Item = i32;
type IntoIter = Test;
fn into_iter(self) -> Self::IntoIter {
self.counter=0;
self
}
}
fn main() {
let x = Test::new(0, 5);
for z in x{
println!("{}", z);
}
}
ЧЯДНТ?
Компилятор на меня ругается, что
conflicting implementations of trait std::iter::IntoIterator
for type Test
:
impl std::iter::IntoIterator for I
where I: std::iter::Iterator;
А, ну тут всё дело, что когда вы реализуете Iterator
то IntoIterator
реализуется автоматически. реализовать его еще второй раз вы не можете, конечно. Что компилятор вам в ошибке и написал:
|
25 | impl IntoIterator for Test {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: conflicting implementation in crate `core`:
- impl<I> std::iter::IntoIterator for I
where I: std::iter::Iterator;
Вот поправил вашу ошибку: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=750d3a6980b6849334d7bc9a9d9abc07
struct Test {
start: i32,
end: i32,
counter: i32,
}
impl Test {
fn new(start: i32, end: i32) -> Test {
Test{start:start, end:end, counter:0}
}
}
impl Iterator for Test {
type Item = i32;
fn next(&mut self) -> Option<i32>{
if self.counter < self.end {
self.counter +=1;
return Some(self.counter-1);
}
else {
return None
}
}
}
fn main() {
let x = Test::new(0, 5);
for z in x{
println!("{}", z);
}
}
А, ок, спасибо большое. Но это не отменяет специальной головной боли при размышлении про IterType и Type в impl IntoIterator
. Это точно не "простая кривая вхождения".
Еще, пока видимо не очень прямо удобным местом является малое количество сред разработки. Неплохо ведет себя IntelliJ с модулем для rust, но иногда не справляется с подсказкой сложных типов. Однако можно писать и в Notepad++ — умный компилятор с гарантиями предупредит вас о ошибках в коде.
Visual Studio Code с расширениями rust-lang и CodeLLDB является прекрасной средой разработки.
VSCode работает через RLS который просто ужасен. Есть превосходная альтернатива от товарища Кладова, но она наверное даже не преальфа до сих пор.
Я лично пользуюсь идеей с плагином, 90% нужного функционала в ней присутствует. Иногда подтупливает + не все рефакторинги есть, да и со сложными макросами ей тяжело, но в остальном всё ок.
Может и преальфа и его нужно компилить вручную, но работает он намного лучше чем rls
2) Идея не умеет дебажить Rust. Умеет CLion, но он в отличии от идеи платен.
- тем что он тормозит и отваливается. У кладова в целом достаточно написано в разделе мотивации для его проекта
- а) Фишка в том что раст обычно дебажить и не надо. Пару раз когда мне это нужно было это как ни странно был сегфолтящийся плюсовый код и clion мне в любом случае нужен был чтобы понять что там происходит
- б) Если бы у меня не было clion я бы все равно писал в идее, а дебажился бы раз в полгода в vscode когда совсем припрет.
Но вызвало улыбку что о таком замечательном Rust написали ребята из 1С-Битрикс. Для меня 1С и Битрикс являются кака бы полной противоположностью Rust.
Может через пару лет 1С язык заменит Rust?
Это было бы очень неожиданно и интересно посмотреть!
внезапно вы узнаете, что используете монадические вычисления в вышеупомянутых цепочках, которые разумно обрабатывают ошибки, однако тут нет умничания с терминами из теории категорий
Это просто к 1.0 не успели скрестить HKT и Borrow Checker, а теперь вот сидим с ad-hoc решениями.
Проблема в том, что в текущих типах эта проблема вообще не решается. Коротко от лодочника: https://twitter.com/withoutboats/status/1027702531361857536?s=21
Так что есть мнение что чтобы это так работало нужно было прям вообще сильно иначе всё делать. Ну или жить с "почти" монадами, как впрочем и хаскель живет с "почти" категорией Hask.
Почему язык назвали "ржавчиной"??
https://stackoverflow.com/questions/16494822/why-is-it-called-rust
Если коротко, то он называется в честь ржавчинного грибка (а не оксида железа) с очень интересными свойствами, которые восхищают одного из разработчиков языка.
высоконагруженные проекты на Java работают с регулярными паузами по несколько секунд, которые нельзя убрать никак
Надо чтобы руки ну очень сильно росли из задницы, чтобы получить паузы по нескольку секунд.
В целом статья никак не оправдывает свой кричащий заголовок.
Странно, что вы не упомянули нативную поддержку работы с AST из коробки — немного криво и косо, но авторы языка подумали (!) об этом, что явно выгодно выделяет Rust из когорты других языков, как по-настоящему пригодный к метапрограммированию.
Как соединили несоединимое и впихнули невпикуемое, что не удавалось сделать за последние 50 лет. Оказалось, возможно, если...… запретить все потенциально небезопасные конструкции.
Выразимость эффективной логики программы в терминах безопасных конструкций никто никогда не гарантировал
Выразимость эффективной логики программы в терминах безопасных конструкций никто никогда не гарантировал
Мне кажется выразительность ником образом не теряется. Теряется всегда производительность и возможность делать какие-то трюки которые опять же чаще всего нужны для максимальной производительности или максимально оптимального использования ресурсов (памяти, шины, сети и проч).
Мне кажется выразительность ником образом не теряетсякак же не теряется то, если я не могу например иметь константную и мутирующую ссылку на объект одновременно, делать неявные преобразования типов или кидать исключения?
Спору нет, этот функционал пожертвован ради безопасности. Выгода такого размена… неоднозначна.
кидать исключения
Это с безопасностью никак не связано. Есть паника — несколько урезанное подобие исключений. Причём "неполноценность" паники просто из-за дизайна языка, который подталкивает к другому способу обработки ошибок.
делать неявные преобразования типов
И их отсутствие — это просто замечательно.
Вон тут ниже очередной растовитянин (который, емнип, не написал ни строчки прода на расте), высмеивал гошника за комментарий в духе «потому что не нужно». Вот в расте кучи всего нет… «потому что не нужно».
Несчастные функциональщики без двусвязных списков всю жизнь живут, и ничё.
Не потому, что не нужно. А потому что мутабельность нужно выжигать каленым железом.
Не потому, что не нужно. А потому что мутабельность нужно выжигать каленым железом...… потому что не нужно?
Функционым яп иммутабельность нужна так же, как полиморфизм объектно-ориентированным. Мы же говорим про императивный rust
Нет :)
Потому что иммутабельность делает сборку мусора ноубрейнером. Ну, помимо того что переключение между процессами в шедулере ОС могло бы ускориться примерно в тыщу раз.
Я вот не помню когда последний раз мне вообще был нужен двусвязный список. А вот невозможность выразить отношение владения в моём C# мешает мне регулярно.
Так что, с моей точки зрения, выразительность у Rust как раз отличная.
Я вот не помню когда последний раз мне вообще был нужен двусвязный список.
Я вот со скриптовых языков пытался ворваться в rust (возможно, что в этом и проблема, и такие вещи решаются как-то иначе в rust), но невозможность построить банальное дерево с отношениями вида дети-родитель сломала мой уютный мирок, и покоробило мой не готовый к такому повороту событий мозг. Я не говорю хорошо это или плохо — у меня не достаточно опыта в таких вопросах.
Субъективно: мне это доставило огромные неудобства.
И объективно: уверен, что я не один такой.
Погуглив, я нашел решение с ареной, но уж чего-чего, а выразительности оно не прибавляет от слова совсем.
Еще есть решение с unsafe. Но в растономиконе, прямо сказано "ататата использовать unsafe, если вы его до конца не понимаете".
И да есть решение с биндами на С++. Которые у меня просто не завелись, то ли в виду сырости самого крейта, то ли ввиду рассинхрона версий зависимостей.
В итоге мой маленький череп не выдержал, и я написал все на JS. Производительность, конечно, гуано. Но работает месяцами и не течет.
Всё выливается в довольно забавную сценку в моей голове:
— Как мне сделать двусвязный список?
— Вам не нужен двусвязный список — говоря это, собеседник
делает джедайский пас рукой.
— Не вам решать, что мне нужно — невозмутимо продолжаю я.
Мой собеседник становится огорчен и фальшиво суетлив, как агент по продажам который вдруг понял, что потенциальный клиент никогда не станет реальным.
— Тогда вам rust вам не поможет — с этими словами он утыкается в монитор, полностью потеряв ко мне всякий интерес, и что-то вдумчиво вводит в терминал.
— Вот и я о том же…
И я огорчен, что мой собеседник не смог мне помочь. а у самого в голове проскакивает мысль: Так зачем ссорится, если обо всём уже договорились? Ну — значит не моё.
Банальное дерево с отношениями "дети-родитель" банально же строится через Rc/Weak, и двусвязный список ему не нужен.
Проблемы в Rust начинаются в тот момент, когда вы пытаетесь сделать чуть оптимальнее за счёт отказа от Rc. Но в данном случае это не важно, поскольку даже с Rc получается куда эффективнее чем в Javascript.
Кстати, вы вот эти реализации смотрели?
https://doc.rust-lang.org/std/collections/struct.LinkedList.html
https://docs.rs/trees/0.2.1/trees/
Но в данном случае это не важно, поскольку даже с Rc получается куда эффективнее чем в Javascript.а если хочется максимально эффективно?
doc.rust-lang.org/std/collections/struct.LinkedList.htmlемнип двусвязный список в расте реализован через индексы нод арена-аллокатора c O(n) ассимптотикой вставки. Тогда двусвязный список действительно не нужен, ведь теряется его единственное преимущество над вектором.
Банальное дерево с отношениями "дети-родитель" банально же строится через Rc/Weak, и двусвязный список ему не нужен
Я уже сейчас и не вспомню, но на слабых ссылках там тоже какие-то проблемы.
На момент моих попыток "врыва" (начало 2017) trees еще не было.
Что касается двусвязного списка, то он тут действительно не при чем. Просто проблема мне показалась схожей (циклические ссылки, насколько я могу понять). Только по этому и зацепился. Хотя даже больше потому, что когда я пришел в сообщество со своими вопросами, мне прям слово в слово сказали: Я вот не помню когда последний раз мне вообще был нужен двусвязный список были нужны такие деревья. Прям дежавю ощутил.
Просто проблема мне показалась схожей (циклические ссылки, насколько я могу понять).
Это только кажется. У двусвязного списка оба указателя — одноранговые, и им невозможно назначить правильное отношение владения. Элементами списка владеет сам список, но у него нет указателей на большинство элементов.
У дерева же родитель владеет детьми (ну или, в некоторых задачах, дети владеют родителем — но такое дерево делается ещё проще), а потому через Rc/Weak дерево реализуется тривиально.
вы вообще не поняли посыл.
Возможно, но верно и обратное.
Нет (привычных) исключений — отлично, обработка ошибок на Result
мне больше понравилась, когда распробовал.
Нет неявных преобразований? Я бы решил точно так же. И этот аргумент можно повернуть назад: если для своих типов в С++ ещё можно explicit использовать, то для встроенных приходится мириться с той логикой, которая зашита в язык. И да, которую выбрали за меня.
Я вообще-то долгое время на С++ писал и отдельные вещи в расте поначалу вызывали раздражение. И сказать хочу, что и там и там разработчики (или комитет) решают за нас. И там и там можно написать proposal/RFC и попытаться повлиять на дизайн языка. И, наконец, и там и там есть свои компромиссы. Мне далеко не всё нравится в расте, но в С++ таких вещей (для меня) больше.
Ну и в плане выразительности тягаться с языками с GC дело неблагодарное.
Вот в расте кучи всего нет… «потому что не нужно».
Не сказал бы. В том смысле, что упираться в ограничения бороу чекера несколько недальновидно. Есть более интересные/важные вещи, которых пока не хватает. Вот только работа над ними ведётся.
Ну и в официальных обсуждениях никто не говорит "не нужно" — есть аргументация и она, как правило, убедительная.
Нет (привычных) исключений — отлично, обработка ошибок на Result мне больше понравилась, когда распробовал.это субъективное мнение. Я пишу только факты. Например, обработка ошибок через исключения производительнее для позитивного сценария.
если для своих типов в С++ ещё можно explicit использовать, то для встроенных приходится мириться с той логикой, которая зашита в язык. И да, которую выбрали за меня.любой встроенный тип с++ можно обернуть, а вот с борроучеккером раста уже не ничего не сделаешь. Плюс, неявный апкаст без потери точности (а-ля u32->u64 или f32->f64) вести к вычислительным ошибкам не может — почему бы не разрешить хотя бы его? Почему бы не разрешить введение неявного каста для пользовательских типов?
Ну и в плане выразительности тягаться с языками с GC дело неблагодарное.Выразительность языка с GC не является недостижимой для системного. Например, можно сравнить те же плюсы с go.
Ну и в официальных обсуждениях никто не говорит «не нужно» — есть аргументация и она, как правило, убедительная.«не нужно потому что ...»?
это субъективное мнение. Я пишу только факты. Например, обработка ошибок через исключения производительнее для позитивного сценария.
Да, но то, что сигнатура с Result даёт больше информации — это тоже факт. А вот если мы начнём считать насколько быстрее или насколько это дополнительная информация полезна, то быстро скатимся к субъективному.
любой встроенный тип с++ можно обернуть
Ага, это будет просто невероятно удобно. Мне кажется, что это аргумент одного порядка с "двухсвязный список можно написать через unsafe и указатели". Разве что список проще в библиотеку спрятать и пользоваться при необходимости. А со своими обёрнутыми числами придётся страдать на границе с любой библиотекой.
Плюс, неявный апкаст без потери точности (а-ля u32->u64 или f32->f64) вести к вычислительным ошибкам не может — почему бы не разрешить хотя бы его?
Полагаю, что ради явности.
Почему бы не разрешить введение неявного каста для пользовательских типов?
"Полунеявное" преобразование, кстати, есть — через From/Into трейты. То есть, можно написать функцию так, что она принимает не конкретный тип, а всё, что может быть в него преобразовано. В коде, который вызывает эту функцию, никаких преобразований видно не будет.
Например, можно сравнить те же плюсы с go.
Смешно. И даже тут можно попробовать подобрать примеры, где Go будет выигрышнее смотреться. А может взять какой-нибудь язык поинтереснее и тогда разница будет ещё заметнее.
«не нужно потому что ...»?
Скорее что-то вроде "не очень понятно как это сделать не поломав Х" или "сначала надо стабилизировать Y".
Возможность разделять программу на "опасные" и "безопасные" куски очень полезна. Те же плюсы плохи не тем, что там наворотить можно, а тем, что нужно 100% быть аккуратным.
В расте можно быть аккуратным только если unsafe написал.
Но это не мешает Box быть более эффективным, нежели unique_ptr
И совсем мутная ситуация с пакетным менеджером. Даже в Node.js и python он есть.
Протому что не нужно. В реальности за качеством кода там никто не следит. Все на ваш страх и риск. Плюс если следящему за пакетом вдруг стало наплевать — система поддержки пакетов будет старые версии держать годами.
В Go прямо с github и т.п. можешь выкачивать командой go get github.com/чё-надо.
До этого выполните команду go mod init — и за версиями будет следить Go сам, не допуская того, чтобы пакет со сменившимся API вас всё сломал.
Протому что не нужно.
Ну как всегда, всё чего нет объявляется нинужным.
Зелен виноград как он есть.
Ну как всегда, всё чего нет объявляется нинужным.
Потому что есть, но люди, пробежавшие по верхушкам обзорные статьи по языку, думают, что нет.
Ну так если бы вы написали "вы ничего не поняли, вот так можно сделать пакетный менеджер" это был бы один разговор.
Но ваш вариант "вы ничего не поняли, это нинужна" — совсем другой.
В статье есть над чем посмеяться.
Категоричные выводы по поводу того, что действительности не соответствует. Для многих языков.
В статье полно слабых мест, на которые уже обратили внимание выше. Можно было бы вполне конструктивно найти недостатки в том, что написали.
А не брать сомнительный тезис и возводить его в ранг абсолюта.
Сложность в том, что текст подается на любом языке, хоть на китайском, хоть на арабском.
Спрашиваю потому что на C++ реализовывать уже задолбался, а скорость нужна, так что руби/питон слишком медленные, нужна именно вставка.
Ржавые строки изкоробки юникодные, но для графемных кластеров (а, как я понимаю, тут нужны они) надо будет https://docs.rs/unicode-segmentation подключить.
Rust для веб-разработчика — быстрый старт и стремительный полет