Как стать автором
Обновить
1
0
Ivan Dubrov @WFrag

Пользователь

Отправить сообщение
Да, но бывает, что динамическая диспетчеризация предпочтительнее. Особенно в крупном проекте.

Понятно, что пример совершенно искусственный.

Тут есть небольшая проблема, если я правильно понимаю, &FooBar привести к &Foo не получится.


Например, если run_foo вдруг захочет вызвать другую функцию, которой нужен &Foo. Или вернуть &Foo в составе какой-то структуры.


В первом случае ещё можно выкрутиться как-то так (ну или через newtype):


impl <'a> Foo for &'a FooBar {
    fn foo(&self) {
        FooBar::foo(*self)
    }
}

А вот вернуть из функции &Foo, если дан &FooBar уже никак (впрочем, решается тривиально через добавление функции fn as_foo(&self) -> &Foo на FooBar).

Это пока чего-то более серьёзного не захочется.

До JUnit в Java ещё пока не дотягивает, на мой взгляд. Например, не хватает возможности удобно писать data-driven тесты. Так, чтобы 1 файл с данными = 1 тест, например; с возможностью запуска индивидуальных тестов.

Какие-то движения вокруг custom test frameworks вроде происходят, но, к сожалению, нет времени, чтобы во всём этом разобраться.

Пока выкручиваюсь с proc macro, который сканирует файлы и создаёт #[test] функцию на каждый файл, выглядит примерно так (второй параметр — регулярное выражение для матча на «основной» файл теста, остальные параметры — шаблоны-подстановки по этому регулярному выражению):

// запустится с A.input.txt / A.output.txt, B.input.txt / B.output.txt, и.т.д.
#[datatest("tests/test-cases", r"^(.*)\.input\.txt", r"$1.output.txt")]
fn sample(input: String, output: String) {
  assert_eq!(format!("Hello, {}!", input), output);
}


Можно, конечно, сделать так, чтобы все эти тесты в одной #[test] функции выполнялись, но тогда очень неудобно с этими тестами работать. Например, если делается рефакторинг и половина тестов поломана — хочется возможности запустить один тест, чтобы начать с чего-то мелкого. Плюс, хочется видеть все поломки, а не первую. И так далее.

Может быть, можно как-то через кишки модуля test выкрутиться?..

P.S. Похоже, можно попробовать через «harness = false» в Cargo.toml. Пора снова переписывать фреймворк :D
Почти: intellij-rust.github.io :D

P.S. Не идеален, но работает сносно (у нас проект на ~100 KLOC строк кода). Некоторые функции работают очень медленно (например, «найди мне все реализации этого типажа»).
Писал в основном на Java более 10 лет (другие языки использовал наскоками, по мере необходимости).

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

С владением в Rust поначалу было непривычно, но процентов 80, наверное, освоил довольно быстро (простые случаи). Более глубокое понимание на уровне интуиции, наверное, выработалось где-то через полгода, особенно после разработки нескольких вариантов API типа reflection в Java (обобщенный обход данных заданных некоторой схемой).

Из необычного было то, что мы используем Rust совсем не как системный язык программирования, а для самого что ни на есть энтерпрайза, фактически как аналог Java / .NET. Поэтому, сложно сказать, какая часть трудностей из-за языка как такового, а какая — из-за особенностей его применения. Часть сложностей была, возможно, из-за недостатка опыта (у меня) в обоих областях: и в знании Rust, и в знании предметной области.

Приходилось проявлять, гм, смекалку. Даже, не побоюсь признаться, приходится заимствовать некоторые подходы из Java (что порой вызывает удивление коллег).

С точки зрения «разгона» команды, старт у нас был довольно трудным, на мой взгляд, — Rust довольно сложный язык, особенно как второй язык после JavaScript. Но постепенно ситуация исправляется, сейчас у нас примерно человек 8 пишущих активно на Rust. Плюс нам повезло нанять пару человек, имеющих активное участи в Rust экосистеме (в том числе и с позиции менторинга), поэтому я настроен оптимистично :)

У меня нет опыта разработки и поддержки больших систем на динамически типизированных языках (Java отнесём в категорию «условно статически-типизированных»), но пока что наш код на Rust переживает рефакторинги весьма успешно.

Как практик, отвечаю, в Rocket для этого есть аттрибут «rank».

Ничего хорошего в нём нет, но приходится делать, то, что приходится :)
Частично соглашусь.

Эти маршруты действительно красиво выглядят в документации (и в подходящих проектах, наверное), но у нас в итоге всё свелось к фактически одному маршруту на всё подряд (ну, к 4-ем, GET/POST/PUT/DELETE), а дальше мы запрос сами разбираем… (тут возникает справедливый вопрос, зачем нам Rocket, но просто пока времени нет всё на hyper переделать).

Но всё же для разных задач — разные инструменты. Охотно допускаю, что где-то такие маршруты (и Rocket) могут быть удобны. Для более фиксированных API (наша проблема в том, что API очень уж динамичные).
>Для кровавого энтерпрайза — извините, но пока что нет, как бы язык ни был хорош :(

Мы, кстати, энтерпрайз кровавее некуда (healthcare) на Rust делаем (Commure). С чистого листа.
Я год назад делал маленький проект, было плюс-минус без костылей (хотя, наверное, xargo можно за костыль посчитать). Всё компилировалось, прошивалось, отлаживалось, под STM32. По-моему, даже прямо из CLion-а у меня получалось отлаживать, но это не точно. gdb точно работал (или lldb? один из них :) ).

Но там всё постоянно меняется — это один из рисков. С тех пор они там какие-то HAL-ы напилили — про это ничего не знаю.

Моя оценка примерно такая:
1) Для хобби — сойдёт (проекты относительно маленькие), если есть желание. Мне понравился мой опыт, причём это был мой первый проект на Rust.
2) Для новых крупных проектов — если есть желание рискнуть, можно сделать ставку на Rust. Я слышал разные страшные истории от одного разработчика в одной всем известной компании про то, как у них низкоуровневый софт пишется (Raspberry PI, хотя это уже не совсем «микроконтроллер») — Rust бы там, как мне кажется, пришёлся бы в тему.
3) Для средних проектов — не знаю, там, наверное, проще взять готовые библиотеки. Хотя с другой стороны, Rust хорошо встраивается в C, наверное, можно кусочно на Rust делать (но FFI — это вдвойне опасная тема).
Я как-то задумался и полетел вперед наехав на лежачего полицейского. Ни о каком там пробежать даже и речи не шло, приземлился на колени/локти/запястья. Может, конечно, можно как-то натренировать навык спрыгивать и бежать, но без тренировки как-то не вышло…
Это в хорошем варианте. У меня все было не так гладко и с медицинскими последствиями.

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

Где-то в сумме две недели ушло (4 раза по часу, но с перерывами на лечение).

Я встречал истории, и месяцами осваивают.

Самокат только чуть-чуть пробовал, там вроде вообще ничему учиться не надо (ну может скорость подбирать, да на дорогу смотреть).
На самокат можно встать и поехать, а колесо осваивать надо.
Спасибо :) Первое впечатление — стало как надо!
Не-не-не, погоди. Я допустил ошибку, не указав явно, что мысленный эксперимент я предлагаю ограничить исключительно строками (так как про них разговор и шёл).

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

В-вторых, RAII я тоже убирать не предлагаю — это тоже семантика.

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

То есть, никаких новых ошибок бы не было.

Проблема только в том, что такого «оракула» не существует — и именно поэтому мы и имеем некоторое постоянное неудобство при работе со строками (но получаем и преимущества).

Вот, кстати, ещё пример неудобства: users.rust-lang.org/t/extending-lifetime-to-the-whole-function-body/15440

Тоже не особо редкий случай, в общем-то.
Я, кстати, не совсем согласен с этой точкой зрения. У Rust довольно большой потенциал в прикладных задачах. Он безопасный, с адекватной эргономикой и достаточно большими возможностями в плане построения абстракций (товарищи хаскеллисты, не бейте, пожалуйста!).

При этом по законам материалистической диалектики, возможен переход от количества к качеству.

Наприер, когда твое приложение «само по себе» работает в N раз «быстрее» аналогичного на Java, это открывает дополнительные возможности. Там, где была bulk обработка вдруг оказывается целесообразной реалтайм обработка, там где нужен был кластер, можно обойтись одним узлом. Я знаю компании, которых разорили счета с AWS. Можно невооруженным взглядом наблюдать проблемы производительности, например, на мобильных устройствах. Я наблюдал постепенное угасание производительности систем на Java.

Спасёт ли Rust от этого? Не знаю, но у него хороший задел.

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

Значит ли это, что надо RIIR? Нет. Но совсем откидывать Rust, как «системный язык» тоже не стоит.

Как-то так.
Да, но почему тебе интересно владение само по себе?

Если бы был некий магический оракул, который бы убивал объекты моментально с уходом последней ссылки и не имел бы всех недостатков GC или подсчёта ссылок (таких как расход памяти на метаданные, давления на кеш в случае GC), ты бы стал заморачиваться с владением?

Или бы просто сделал так, что String — это изменяемая строка, а &str — неизменяемая, которую можно передавать куда угодно и как угодно хранить (считай, аналог Rc но без расходов на подсчет ссылок).
Всё было бы гораздо проще, если бы std::borrow::Borrow borrow() возвращал бы Borrowed, а не &Borrowed.

У нас сейчас вариант с Cow, на самом деле, потому что плюс-минус пофиг.

Просто человек вот удивлялся, когда это дескать в Rust низкоуровневые вещи постоянно приходится указывать — я говорю, что постоянно, особенно со строками. Я считаю, что с точки зрения логики прикладной программы все эти жонглирования особо смысла не имеют. С точки зрения корректности программы на Rust — разумеется, с точки зрения производительности — да, конечно.
Да я в общем-то и не считаю, что это проблема. Я на автомате такое пишу, новичкам в большинстве случаев компилятор внятно подскажет, а где не подскажет компилятор, там я помогу :)

Но если уж быть честным, то надо признать, что в Rust довольно много вот таких «ритуальных» действий (хотя их количество и сокращают — например, RFC 2005).
Тем не менее, это постоянно вылезает, о чём и был мой комментарий. Компилятор, конечно же, помогает, но это не убирает тот факт, что такие низкоуровневые моменты (потому что к бизнес логике они имеют слабое отношение) нужно указывать постоянно.

Даже если забить болт на оптимальность, всё равно, в применении к строкам, будут постоянные to_string/clone и довольно неуклюжие операции со строками (впрочем, тут format! сильно помогает).

Информация

В рейтинге
Не участвует
Откуда
San Ramon, California, США
Зарегистрирован
Активность