Pull to refresh

Comments 172

Как же удалось отказаться от сборщика мусора?

А вот так! Память в компиляторе начали представлять как ресурс, «владеть» («ownership») которым может одна и только одна переменная.

RAII в C++ (к примеру std::unique_ptr)?
да, я пишу дальше про RAII. Только в Rust логика RAII встроена в сам компилятор и проверяется аффинными типами. Упростили подход и сделали его надежным.
В STL std::unique_ptr написали такие же программисты, что писали RAII в компиляторе Rust. Где причина повышения надежности?

Причина повышения надёжности в том, что в Rust всё подчиняется логике владения и заимствований, за исключением нескольких операций доступных только в unsafe блоках.

Аффинные типы, не?
{
    Foo* a = new Foo{};
    std::unique_ptr<Foo> p1{a};
    std::unique_ptr<Foo> p2{a};
    // Упс, двойное освобождение памяти в конце блока
}


В расте это ошибка компиляции.

Зачем это нужно, если можно создавать объект в std::make_unique?

Случайно, на стыке старого и нового кода. Или просто по незнанию...


Вот, всего месяц назад на 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();
unique_ptr::release() просто так не вызывают

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

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

Все ручное управление типа get() release() если по честному то приравнивается к ансейф расту, т.к. эти методы нету смысла вызывать в с++ коде. А связь с си в расте тоже ансейф. Другое дело что вы можете сказать что в расте ансейф более явный. Но вообще юзе афтер мув более менее отлавливают анализаторы. Но неприятности как раз приходят в менее тривиальных случаях.

Можно заменить release() на передачу куда-нибудь через std::move. Суть в том, что корректная операция для однократно вызываемой лямбды оказывается некорректной для многократно вызываемой, но разницы между ними в с++ нет в отличие от раста.

std::unique_ptr может как указывать на объект, так и быть пустым. В Rust std::boxed::Box всегда что-то содержит.

Да, он еще молод (2 года как стабилизировался)

2 года? Вообще-то расту уже скоро 5 будет, 1 версия вышла в мае 2015. В 2018 создали уже вторую версию, т.н. Rust 2018 Edition, которая фиксит многие огрехи первой версии.

UFO just landed and posted this here
Единственные утешения, идущее от Golang:
встроенные в язык «green threads» (аля корутины), но они есть уже даже в python
компиляция в нативный код, только непонятно, зачем (возможно, чтобы Docker работал) :-)

И совсем мутная ситуация с пакетным менеджером. Даже в Node.js и python он есть.


Где мутность? Так же сто лет уже есть пакетный менеджер и он так же развивается, как развивается NPM или Cargo.
Всё ещё не понятно, как этот пакетный менеджер работает. Cargo и npm имеют свои централизованные репозитории, в которых пакеты строго версионируются, а зависимости указываются в одном месте — packages.json/cargo.toml, а построенное дерево зависимостей фиксируется через lock. Чего-то подобного в go не видно
вот именно, я про это и написал в посте
встроенные в язык «green threads» (аля корутины), но они есть уже даже в python


Треды суперлегкие. + Приложения с одной и та же логикой реализованные к примеру на node.js, python от 100 до 10 раз потребляют больше ресурсов (оперативная память, утилизация процессора) чем программа на Go

Всё ещё не понятно, как этот пакетный менеджер работает.

Предполагалось изначально централизованное хранилище пакетов для всех приложений на машине. Да с версиями они изначально «протупили», но уже как год, полтора в механизм управления зависимостями включено версионировании и возможность хранения зависимостей непосредственно локально с приложением.

Видно с введением go modules

По факту про состояние веб разработки в Rust практически нечего не сказано (состояние тулинга, готовность фреймворков и тд). Возможно название для статьи стоило выбрать другое
Ну да, какая-то вода с рекламным уклоном, был обещан «опыт быстрой реализации высоконагруженного сетевого проекта», а по факту статья чуть менее чем полностью состоит из кучи довольно спорных тезисов про различные языки без единой строчки той самой «реализации».
в следующем посте расскажем детально про реализацию, много контента получилось для восприятия, его важно еще переварить
UFO just landed and posted this here
добавил детали проекта, да, они полезны, согласен
добавил детали про проект и про тулинг, прошу прощения, упустил, а важно, да

Есть актикс, ну и в общем-то всё. Остальные фреймворки далеко не оптимальны и даже на каком-нибудь го можно получить результат быстрее и лучше.


Основная проблема — веб очень сильно базируется на асинхронности, с которой в языке до сих пор всё ужасно. Для стороннего наблюдателя не особо видно, но писать асинхронный код на расте до сих пор очень больно. Думаю это будет продолжаться еще как минимум полгода. Когда асинк/авейт наконец попадет в стейбл, когда библиотеки стабилизируют асинхронное апи, когда токио выползет на версию 1.0, вот тогда что-то можно будет делать.




На расте уже сейчас можно написать самый быстрый веб-сервер из всех языков, тот же бенчмарк веб-фреймворков не даст соврать. Но в процессе этого вы накушаетесь проблем по самое не балуй. На том же го вы получите 30% результата за 10% времени или около того.

да, пока в Rust с async/await библиотеки стабилизируют (c nigth Rust решили не связываться), полгода-год пройдет, пока на текущих futures 1 версии получилось спокойно взлететь
Когда асинк/авейт наконец попадет в стейбл

Уже же.

Емнип он пока в бете. В стебл в следующий релиз.


Но даже без этого, последние батлы async-std против токио, tokio 0.1 vs 0.3, compat::Backward/Forward туда-сюда, невнятные стримы, апишки которые появляются и исчезают с разницей в пару недель, и дока которая при этом отстает на месяцы (и поэтому неюзабельна полностью).


В общем, пока всё плохо.

UFO just landed and posted this here

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 есть в доке, но в альфе выпилили

Ну справедливости hyper всегда хейтили из-за сложности. Если вдруг появится желание поиграться — лучше взять reqwest.

Только я не нашел пример как на нем делать асинк-авейт запросы эффективно. Покажите, пожалуйста, если не трудно.


Когда я его ковырял он поддерживал только синхронные методы. Щас вроде что-то повяилась, но документация как всегда: https://docs.rs/reqwest/0.8.4/reqwest/unstable/async/index.html

а я в проекте не использовал языковые async/await — а сделал подобное через комбинаторы и стримы и не помню проблем никаких
UFO just landed and posted this here
Были конечно. Старался обходить в сторону упрощения. Напрягали области видимости, ибо там вот реально все кажется совсем непривычно. Разумеется теоретическая часть перечитана и понята 3 раза как минимум и все возможные ресурсы (в т.ч. по unsafe rust) по rust тоже прорабатывались, кроме самой неполной спецификации языка. Но вот удалось обойти довольно быстро. В результате вся сетевая логика на комбинаторах потоков — функциональная по сути.
UFO just landed and posted this here
а типы их автоматически выводит IntelliJ IDEA, можно не запариться, они получаются сложными и громоздкими, но зато их не видно :-)

Даже когда идея показывает


= 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 откровенно не взлетел по причине агрессивных «понтов» — чтобы хорошо понять его терминологию, нужно быть, как минимум, кандидатом наук со специализацией в области одной из лидирующих теорий в современной математике: теории категорий.

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

я тоже работаю, но зачем же вводить в язык «аппликативные функторы», как только не от чувства избранности?

А что такого сложного в программистком понимании апликативного функтора?


Кроме того интересно, где это берут на работу хаскелистов, которые говорят о неимоверной сложности апликативного функтора?

хорошо, скажем «гомоморфизм над категориями» :-) это не издевательство над остальным человечеством?

Это относится к программированию на хаскеле примерно никак.

"аппликативный функтор" вещь намного проще, чем например "паттерн мост".

Зашёл сюда оставить (почти) этот комментарий.


По этой же логике растом нельзя пользоваться, не понимая линейную логику.


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

UFO just landed and posted this here
UFO just landed and posted this here
ну так Golang же создавали как замену C и к нему сейчас такое отношение. Он же не высокоуровневый.
UFO just landed and posted this here
А я вам чуть не поверил)
Ну а зачем тогда в Golang напихали строгую типизацию и порезали возможности ООП по мама не балуй, кроме как не сделать из него безопасный C для начинающих. Я так честно думал, пока пост не написал и не почитал комменты :-)
Скорее язык, который подходит для тулчейнов и системных утилиток (где раньше правили балом C/C++ и Python), так как очень простой (без замудренных конструкций, так что даже олдскульный хакер C освоит его довольно быстро), переносимый, быстрее Python и при этом со сборкой мусора.
UFO just landed and posted this here
UFO just landed and posted this here
Да нам тоже пришлось unsafe использовать, к сожалению. Добавил в раздел про проект детали.
Автор, у вас эффект Даннинга Крюгера в чистом виде.

== Rust и почему от такой популярный и востребованный.

зачем здесь эта ссылка, и откуда инфа про популярность и востребованность Rust? Выдаёте свои фантазии за реальность и констатируете их как нечто самоочевидное.

== встроенные в язык «green threads» (аля корутины), но они есть уже даже в python

«green threads» в Go ничего общего не имеют с корутинами питона. Вы бы поизучали вопрос прежде, чем делать высказывания космического масштаба и космической же глупости.

== компиляция в нативный код, только непонятно, зачем (возможно, чтобы Docker работал)

Похоже, у вас проф. левель недостаточно высок для понимания. Спросите у тех. кому понятно. Вам объяснят на пальцах.

== совсем мутная ситуация с пакетным менеджером.

Любая ситуация будет мутной, если смотреть на неё через призму предрассудков и нежелания читать документацию.

Но вы пишите ещё, ваше мнение очень важно для всех нас.
«green threads» в Go ничего общего не имеют с корутинами питона. Вы бы поизучали вопрос прежде, чем делать высказывания космического масштаба и космической же глупости.

это моя любимая тема, в которой специализируюсь последние несколько лет, увы

Похоже, у вас проф. левель недостаточно высок для понимания. Спросите у тех. кому понятно. Вам объяснят на пальцах.

я знаю почему — модно :-)
это моя любимая тема, в которой специализируюсь последние несколько лет, увы


Насколько я понимаю, комментатор выше имел в виду что горутины в go реализуют вытесняющую модель многозадачности, а корутины в python — кооперативную.
В горутинах нам не надо явно писать yield и решать, где приостановить одну горутину и возобновить другую — шедулер языка сам этим занимается.
Поэтому это две большие разницы, и говорить, что питоновские корутины это аналог горутин и то что они «есть уже даже в python» — некорректно.
Услышал, но не совсем согласен. Ведь всё это и в Go и в Python — виртуальная многозадачность в пространстве пользователя, выполняемая на N-честных потоках операционной системы. В Java, на честных потоках ее можно реализовать как угодно же, не говоря про Rust.
Ведь всё это и в Go и в Python — виртуальная многозадачность в пространстве пользователя


В Go — реальные потоки по числу ядер процессора (можно ограничить в настройках программы и использовать не все ядра, если тебе надо) умножить на «виртуальную» в пределах одного ядра.

То есть если у вас 16 ядер, то на Go вы можете запустить хоть 10 000 потоков. При этом в действительную параллель вытесняющим образом будут выполнятся только 16, что обеспечаны железом, конечно же.

Остальные будут переключаться кооперативным образом с этими 16-ю (например, на функциях синхронизации/ввод-вывода).

Насколько я знаю, в Go многозадачность очень даже кооперативная — просто yield скрыт под капотом и не светится. Т.е. горутины прерываются только на ожидании асинхронных операций типа чтения-записи сокета или ожидания на канале.

Что, в принципе, прямой путь к дедлоку, если у нас N ядер, N горутин, и каждая чем-то очень тяжелам занята.

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

В вытесняющую многозадачность умеет только шедулер ОС, вот только проблема в том, что он медленный. Поэтому и используются "грин-треды", которые так не умеют, зато быстрее.

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

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 для этого есть средства. Но да, об этом надо подумать разработчику, и да, рано или поздно он подумать об этом забудет. Но везде можно выстрелить себе в ногу, это лишь одна из возможных ошибок, при том не самая фатальная.

А откуда возьмётся именно дедлок? Закончатся тяжелые задачи, и планировщик получит таки управление.

Настоящего дедлока не будет, конечно. Но когда к вам в queue сыпется 10К сообщений в секунду, подождать минутку — вот вообще не вариант.

К дедлоку вряд ли. К starvation, да, возможно, но это уже совсем другой разговор.


Edit: написали выше, писать комменты через два дня после загрузки страницы без их обновления — плохая идея.

Ну да, ну да, не будет дедлока, я игзаджерировал (пардон май френч :)

При всей моей любви к Rust, от этой статьи только негативные впечатления.
В основном из-за того, что автор много рассуждает о языках, о которых знает, судя по всему, только понаслышке.
Ну или знает не понаслышке, но очень уж поверхностно.
можете привести конкретный пример, попробую ответить конкретно, подискутируем?
В нормальном виде история становления ЯП была в книге
Кауфман В.Ш. Языки программирования: концепции и принципы
А моя настольная книга — «Effective Java» Джошуа Блоха. Без воды и правда, чистая правда :-)
можете привести конкретный пример, попробую ответить конкретно, подискутируем?

Ну вот, например.


(все же знают, что «npm» в Node.js написан на «rust»?)

Насколько я знаю, на расте там только сервис авторизации переписывали.

А вы пробовали дальше заголовка читать?

А я в первом комментарии написал, что там было на самом деле. Можете перечитать его. Такие ответы вызывают в целом сомнения в компетенции и адекватности автора.

пофиксил, Вы правы, где-то еще есть сомнения, давайте обсудим, пишите плз.
но Вы правы, я допустил неточность в масштабе применения Rust в npm, пофиксил в посте, прошу прощения

Не подумайте, что придираюсь, но, честно говоря, формулировка


все же знают, что части «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. Это точно не "простая кривая вхождения".

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

Это оттого, что вы давно не были в Питере. Вот известная сенён-парочка из JB живёт и работает в Питере, и они для Rust написали аж целый плагин для Idea. А жизнь на Кипре не располагает к серьёзным компьютерным наукам.
Сенён-парочка? Известная? Я что-то пропустил?
я тоже гуглить начал, стало прям интересно :-)
Еще, пока видимо не очень прямо удобным местом является малое количество сред разработки. Неплохо ведет себя IntelliJ с модулем для rust, но иногда не справляется с подсказкой сложных типов. Однако можно писать и в Notepad++ — умный компилятор с гарантиями предупредит вас о ошибках в коде.


Visual Studio Code с расширениями rust-lang и CodeLLDB является прекрасной средой разработки.

VSCode работает через RLS который просто ужасен. Есть превосходная альтернатива от товарища Кладова, но она наверное даже не преальфа до сих пор.


Я лично пользуюсь идеей с плагином, 90% нужного функционала в ней присутствует. Иногда подтупливает + не все рефакторинги есть, да и со сложными макросами ей тяжело, но в остальном всё ок.

Может и преальфа и его нужно компилить вручную, но работает он намного лучше чем rls

1) Чем же RLS ужасен?
2) Идея не умеет дебажить Rust. Умеет CLion, но он в отличии от идеи платен.
  1. тем что он тормозит и отваливается. У кладова в целом достаточно написано в разделе мотивации для его проекта
  2. а) Фишка в том что раст обычно дебажить и не надо. Пару раз когда мне это нужно было это как ни странно был сегфолтящийся плюсовый код и clion мне в любом случае нужен был чтобы понять что там происходит
  3. б) Если бы у меня не было clion я бы все равно писал в идее, а дебажился бы раз в полгода в vscode когда совсем припрет.
Отличная статья.

Но вызвало улыбку что о таком замечательном Rust написали ребята из 1С-Битрикс. Для меня 1С и Битрикс являются кака бы полной противоположностью Rust.

Может через пару лет 1С язык заменит Rust?
Это было бы очень неожиданно и интересно посмотреть!
внезапно вы узнаете, что используете монадические вычисления в вышеупомянутых цепочках, которые разумно обрабатывают ошибки, однако тут нет умничания с терминами из теории категорий

Это просто к 1.0 не успели скрестить HKT и Borrow Checker, а теперь вот сидим с ad-hoc решениями.

Ну это норма. В хаскеле к первой версии стандарта тоже не успели скрестить System FC и линейные типы, сейчас тоже сидим с костылями, и в лучшем случае получим просто чуть больше костылей.

Проблема в том, что в текущих типах эта проблема вообще не решается. Коротко от лодочника: https://twitter.com/withoutboats/status/1027702531361857536?s=21


Так что есть мнение что чтобы это так работало нужно было прям вообще сильно иначе всё делать. Ну или жить с "почти" монадами, как впрочем и хаскель живет с "почти" категорией Hask.

Ну или жить с "почти" монадами, как впрочем и хаскель живет с "почти" категорией Hask.

Это выпилить хотят (по крайней мере, лично я хочу).

высоконагруженные проекты на Java работают с регулярными паузами по несколько секунд, которые нельзя убрать никак

Надо чтобы руки ну очень сильно росли из задницы, чтобы получить паузы по нескольку секунд.
Обратите внимание от лица какой компании публикация.
Вот у меня есть такой проект на Apache Mahout с несколькими матрицами 10 на 10 миллионов с хэш-мапами с линейным пробингом и чего я только с GC разных версий не делал, регулярно какая-то муть со сборкой мусора, иногда вызывающая такие вот неприятные паузы. На других проектах прям вот STW нет, но замедления регулярные. Еще отлично тормозит на несколько секунд регулярно любимая нами среда IntelliJ IDEA ;-)
Странно упоминать библиотеку предназначенную для batch-processing'а в контексте обсуждения web-разработки. Или вы пытаетесь её использовать прямо в процессе обработки http-запроса? IDEA же тормозит не столько из-за Java, сколько из-за семантического анализа. Некоторые, написанные на C, компиляторы на той же задаче впадают в такую задумчивость, что можно смело на перекур отправляться.
Смотри, пост для текущих веб-разработчиков, пишущих на PHP в основном, чтобы задумались об использовании Rust для решения текущих задач.
это просто лютый треш, столько бреда в одном месте я давно не читал
Полностью согласен. Статья содержит кучу противоречивого бреда и хочется оспорить буквально каждый абзац по знакомым мне технологиям. Но воздержусь
В целом статья никак не оправдывает свой кричащий заголовок.
Можно, пожалуйста, конкретнее, я готов пояснить неясные в посте моменты.
Написано так словно автор собрал и более грамотно описал всё что я думаю о расте ) Хотя я его ещё не собрался пощупать, но выглядит всё именно так.
старался, да, надеюсь пригодится!
python/java/c++/nodejs/php — все плохие) один Rust хороши )) какое-то такое впечатление от статьи…
нет, выводов не делаю, просто факты и размышления

Странно, что вы не упомянули нативную поддержку работы с AST из коробки — немного криво и косо, но авторы языка подумали (!) об этом, что явно выгодно выделяет Rust из когорты других языков, как по-настоящему пригодный к метапрограммированию.

Как соединили несоединимое и впихнули невпикуемое, что не удавалось сделать за последние 50 лет. Оказалось, возможно, если...
… запретить все потенциально небезопасные конструкции.

Выразимость эффективной логики программы в терминах безопасных конструкций никто никогда не гарантировал
Выразимость эффективной логики программы в терминах безопасных конструкций никто никогда не гарантировал

Мне кажется выразительность ником образом не теряется. Теряется всегда производительность и возможность делать какие-то трюки которые опять же чаще всего нужны для максимальной производительности или максимально оптимального использования ресурсов (памяти, шины, сети и проч).
Мне кажется выразительность ником образом не теряется
как же не теряется то, если я не могу например иметь константную и мутирующую ссылку на объект одновременно, делать неявные преобразования типов или кидать исключения?

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

Это с безопасностью никак не связано. Есть паника — несколько урезанное подобие исключений. Причём "неполноценность" паники просто из-за дизайна языка, который подталкивает к другому способу обработки ошибок.


делать неявные преобразования типов

И их отсутствие — это просто замечательно.

вы вообще не поняли посыл. Выйдите на секунду из манежа пресловутой безопасности и посмотрите шире. Речь про выразительность — совершенно иной аспект языка программирования. Ну вот нельзя в расте написать двусвязный список. Почему? Потому что борроучеккер ради безопасности. Ну нельзя в расте кинуть исключение. Почему? Ради унификации механизмов обработки ошибок. Нет в расте неявных преобразований типов. Почему? Потому что за вас решили что это вам не нужно. Каждый такой компромисс в дизайне языка жертвует выразительностью в угоду безопасности и унифицированности. Хорошо это или нет — полностью субъективно. Выразительность падает — факт.

Вон тут ниже очередной растовитянин (который, емнип, не написал ни строчки прода на расте), высмеивал гошника за комментарий в духе «потому что не нужно». Вот в расте кучи всего нет… «потому что не нужно».

Несчастные функциональщики без двусвязных списков всю жизнь живут, и ничё.


Не потому, что не нужно. А потому что мутабельность нужно выжигать каленым железом.

Не потому, что не нужно. А потому что мутабельность нужно выжигать каленым железом...
… потому что не нужно?

Функционым яп иммутабельность нужна так же, как полиморфизм объектно-ориентированным. Мы же говорим про императивный rust

Нет :)


Потому что иммутабельность делает сборку мусора ноубрейнером. Ну, помимо того что переключение между процессами в шедулере ОС могло бы ускориться примерно в тыщу раз.

иммутабельность с точки зрения производительности тоже не безгрешна. Например, необходимо копированть контейнер ради изменения одного его элемента, или вводить хитрые COW иммутабельные структуры данных (про них есть много докладов), в которых тоже есть где теряться производительности.

Я вот не помню когда последний раз мне вообще был нужен двусвязный список. А вот невозможность выразить отношение владения в моём 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) ассимптотикой вставки. Тогда двусвязный список действительно не нужен, ведь теряется его единственное преимущество над вектором.
а если хочется максимально эффективно?

То надо придумать правильную абстракцию и реализовать её с помощью unsafe.


емнип двусвязный список в расте реализован через индексы нод арена-аллокатора

Да нет, он использует обычный unsafe код с указателями.

Банальное дерево с отношениями "дети-родитель" банально же строится через 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 вас всё сломал.
Протому что не нужно.

Ну как всегда, всё чего нет объявляется нинужным.


Зелен виноград как он есть.

Ну как всегда, всё чего нет объявляется нинужным.

Потому что есть, но люди, пробежавшие по верхушкам обзорные статьи по языку, думают, что нет.

Ну так если бы вы написали "вы ничего не поняли, вот так можно сделать пакетный менеджер" это был бы один разговор.


Но ваш вариант "вы ничего не поняли, это нинужна" — совсем другой.

Комментарий умышленно написан в форме стёба над теми, кто уже делает неверные выводы, еще не понимая сути вещей.

В статье есть над чем посмеяться.

Категоричные выводы по поводу того, что действительности не соответствует. Для многих языков.

В статье полно слабых мест, на которые уже обратили внимание выше. Можно было бы вполне конструктивно найти недостатки в том, что написали.


А не брать сомнительный тезис и возводить его в ранг абсолюта.

Неужели не понятно, что я написал пост для фана, чтобы посмеяться еще раз над тем, что творится и сделать следующий шаг в верном направлении :-)
Спасибо за статью. Как с языка сняли, почти все тоже самое у меня в голове крутится об этих языках и техниках вокруг них, хотя опыта поменьше чем у Вас. Кажется, что это очевидные вещи должны быть для других разработчиков в пользу выбора Rust. От себя добавлю некоторые мысли для других: представьте себе серьезный военный конфликт (не глобальный, но очень серьезный), Системы должны быстро оценивать ситуацию, как можно быстрее реагировать, уметь это делать параллельно, т.е. оценивать ситуацию по разным параметрам и нельзя допустить, чтобы программа упала в самый ответственный момент, должна быть надежной, без утечек, без гонок за ресурс, и т.д. Этот большой проект нужно сделать быстро, время на тестирование минимальное, повторюсь, система должна быть очень быстрой, параллельной, надежной, ресурсы НЕ безграничны (это вам не amazon)… Какой язык выберите, уважаемые? Rust. Вопрос закрыт.
а что, rustc + llvm уже сертифицированы для использования военными?
Уважаемые выберут, скорее всего, что-нибудь вроде SPARK/Ada, но никак не растик.
А вот такой вопрос: допустим я вызываю Rust из моего кода на Ruby (проект Rutie) — смогу я работать с юникод строками из коробки? Нужно брать по одному символу, определять — буква-ли это (не только английская и русская!), что-то с ней делать и брать следующую.
Сложность в том, что текст подается на любом языке, хоть на китайском, хоть на арабском.
Спрашиваю потому что на C++ реализовывать уже задолбался, а скорость нужна, так что руби/питон слишком медленные, нужна именно вставка.

Ржавые строки изкоробки юникодные, но для графемных кластеров (а, как я понимаю, тут нужны они) надо будет https://docs.rs/unicode-segmentation подключить.

Спасибо, попробую в понедельник (отдыхать нужно даже от своих проектов)
Only those users with full accounts are able to leave comments. Log in, please.