Как стать автором
Обновить

Краткая история Rust: от хобби до самого популярного ЯП по данным StackOverflow

Время на прочтение 8 мин
Количество просмотров 38K
Всего голосов 76: ↑69 и ↓7 +62
Комментарии 116

Комментарии 116

Крайне сложно представить себе реальный проект в котором писать Rust было бы экономически оправдано. Вся структура языка заточена на то, чтобы вынуждать человека указывать достаточно низкоуровневые подробности не тогда, когда это действительно нужно, а ПОСТОЯННО. Исключение где Rust может быть реально полезен могут составлять только Embedded и hard real-time системы. Однако это капля в море разрабатываемого софта.
НЛО прилетело и опубликовало эту надпись здесь
Разница всё же небольшая есть, но если есть опыт в C++, то низкоуровневые вещи легко пишутся «на автомате». А если есть опыт в Scala (OCaml, Haskell), то и высокоуровневые части языка понятны и приятны.
НЛО прилетело и опубликовало эту надпись здесь
Когда есть опыт в OCaml и Haskell, высокоуровневые части не кажутся приятными. Слишком громоздко.
И хреново композятся.
(grammar-nazi-mode компонуются), но по сути согласен

Это смотря какие части имеются в виду :) Типажи, итераторы и подобное — вполне себе. Более высокие материи в ржавчине, да, не так красиво выглядят, конечно, но без толстого рантайма и не понятно как их органично реализовать можно.

>Не вижу большой разницы при написании кода на Расте и Джаве, не говоря уже про С.
Сравните ради интереса пару типовых приложений. К примеру word-count и убедитесь в том, что на Rust потребуется в 2-3 раза больше кода, чем на многих других языках.
НЛО прилетело и опубликовало эту надпись здесь
Но все таки сложно спорить с тем, что Rust очень сильно перегружен синтаксически. В нем очень много специальных символов, ворох которых существенно усложняет чтение. И в этих условиях сравнение Go только против Rust работает — при всей мощи системы типов Rust он не выглядит таким уж лаконичным и читабельным, а даже наоборот. По крайней мере Go синтаксически довольно примитивен и специально, чтобы его читать можно было как дважды два. А Rust при каждом случае, когда я сталкиваюсь с его кодом, вызывает похожие чувства, как перегруженный синтаксическими изысками C++. Ни в какое сравнение это не идет с Java, Go или Python.

Но Rust в итоге это можно простить, потому что на нем сказывается его специализация на thread-safety и те жертвы, на которые он идет ради этого. Только все это делает его несколько узкоспециализированным, и сложно его представить как полноценную drop-in замену тому же C++. Он способен его заменить разве что по мощности синтаксиса и скорости выполнения.
Вот подумал ровно так же. Пробовал знакомиться с Rust но синтаксис меня просто раздражал, так и не осилил. Решил дальше дружить с Go
Мы пишем на Rust систему, которую традиционно бы писали на Java (условный «кровавый энтерпрайз»). После пары месяцев активного написания, когда основные моменты Rust сходу понимаешь, каких-то особых трудностей нет.

Приходится больше думать, например, о владении объектов (в Java можно практически вообще не думать), но в целом чувствую себя вполне продуктивным. И уж Java-то по количеству кода грех не уделать — в Rust более мощная система построения абстракций (типажи, макросы, аттрибуты, более мощные параметры типов).

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

Любой язык после погружения в него перестанет казаться непонятным. Но я, смотря на него извне, вижу его излишне сложным для чтения. Для меня видится, что такой код будет сложно поддерживать в условиях, когда ты не держишь постоянно в голове его, а периодически возвращаешься к нему спустя какое-то время. Обычно языки со сложным и богатым синтаксисом страдают в этой ситуации. Даже C# вроде бы и то заставляет иной раз погрузиться в код, чтобы вспомнить, чего он такого хитрого делает с его куче синтаксических конструкций. И мне кажется, что Rust сильно ушел в сторону усложнения своего синтаксиса и системы типов, а о читабельности забыл. Не найден баланс. Собственно, в задачах языка этого и нет — safety, speed, and concurrency. Это все конечно замечательно, но это определенно продолжит удерживать rust от роста его доли на рынке. Stackoverflow не показатель ничего в этом плане.
Stackoverflow не показатель ничего в этом плане.

Мне кажется, это как раз показатель. Показатель того, что с ним возникает очень много вопросов.
> Существует лишь два вида языков программирования: те, которые постоянно ругают, и те, которыми никто не пользуется ©

То, что по Rust задают вопросы не означает ничего плохого. Во-первых там действительно есть некоторые концепции, которые непривычны неподготовленному человеку. Уверен, какие-нибудь темплейты после чистого С казались странной конструкцией. Во-вторых я не припомню, чтобы по какому-то «простому» языку на SO не задавали бы вопросы. Блин, там есть вопрос «как сложить 2 числа с помощью jQuery» с 7000 апвотами. Означает ли это, что сложить 2 числа в JS сложная задача, с которой у многих проблема?

Мы пишем на Rust систему, которую традиционно бы писали на Java (условный «кровавый энтерпрайз»).


Странный выбор.
Хотя… Селектел тоже писал на Haskell свой «кровавый энтерпрайз». Даже получилось. Потом переписали. Почему-то.

Время покажет, странный выбор или нет.
Время покажет, странный выбор или нет.


Разработчики тут по сравнению с предприятием очевидно в выигрышной ситуации.
Глубокое погружение в язык — полезно.
НЛО прилетело и опубликовало эту надпись здесь
Вы сравнили, не я. И при условии, что в Go этого всего как раз нет, у вас получился примерно одинаковый по объему код. Это не в пользу Rust говорит.
НЛО прилетело и опубликовало эту надпись здесь
Если обьем кода одинаков, а возможностей намного больше


Возможностей выполнить конечную задачу у языков этого типа — поровну совершенно.
Потенциальный путей (вы видимо это обозначаете словом возможности) больше — код лучше не становится, код становится путанее.
НЛО прилетело и опубликовало эту надпись здесь
Не согласен. Дженерики и макросы очень сильно упрощают код. Да и обработка ошибок в Расте мне сильно приятнее.


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

Обработка ошибок — вкусовщина. В go приняли такую схему не потому что авторы не знали о других способах
НЛО прилетело и опубликовало эту надпись здесь
К примеру word-count и убедитесь в том, что на Rust потребуется в 2-3 раза больше кода, чем на многих других языках.
Лаконичность — далеко не всегда достоинство. Перерлюнуть Forth в этом отношении тяжело, но много вы видели программистов на Forth?
>Лаконичность — далеко не всегда достоинство
Ручное управление памятью и пресловутое Memory safety тоже нужно дай бог в нескольких процентах реальных проектов.
Ручное управление памятью и пресловутое Memory safety тоже нужно дай бог в нескольких процентах реальных проектов.


Вот для таких проектов он и создан.
А для прочих — есть более удобные Go, Python и пр.
Ресурс это не только память.

Если с памятью GC кое-как справляется, то вовремя закрывать файлы/сокеты он не умеет. Поэтому у нас в расте всякие Disposable и существуют. В GO полагаю тоже есть какие-то способы полу-автоматически Close вызывать на ненужных объектах. Но когда компилятор не заставляет их закрывать, начинается беда.

Кстати, типовой ответ на это — «так никто не пишет»
Это вы ещё APL не изучали.

А программистов на форте я видел порядочно (и регулярно вижу в зеркале). У форта всего одна проблема — нужно полгода, чтобы переучить мозги действовать в стековой парадигме.

Зато APL — в обычном ASCII не записывается. Слишком уж много там спецсимволов.
Это вы ещё APL не изучали.
Изучал как раз. Я ж по специальности математик, не программист.

Но в APL есть проблема: многие математические алгоритмы записываются коротко и просто, но многие другие, не ложащиеся на матрицы — просто ужасно.

Зато APL — в обычном ASCII не записывается. Слишком уж много там спецсимволов.
Для таких случаев есть J и его потомки.

В любом случае реальную операционную систему на этом сделать нельзя, на rust или forth — можно. Так что это языки для совсем разных ниш.
Ну предела для извращений нет. Была IBM 5100 с микропрограммной реализацией APL. Так что для неё ОС- разумно было писать на APL, ибо ничего другого не было.

Там очень забавный диалект APL был — с файловыми операциями и всем прочим.
в 2-3 раза больше кода, чем на многих других языках

Возможно Вы про какой-то другой Rust говорите? Если мы будем сравнивать с языками, созданными специально для обработки текста, то наверняка с задачей обработки текста они справятся лучше. Но с ними проиграют вообще большинство современных популярных и универсальных языков.


public static void main (String[] args) {

    System.out.println("Simple Java Word Count Program");

    String str1 = "Today is Holdiay Day";

    String[] wordArray = str1.trim().split("\\s+");
    int wordCount = wordArray.length;

    System.out.println("Word count is = " + wordCount);
}

extern crate regex;

fn main() {

    println!("Simple Rust Word Count Program");

    let str1 = "Today is Holdiay Day";

    let spaces = regex::Regex::new(r"\s+").expect("Can't compile regex");
    let word_count = spaces.split(str1.trim()).count();

    println!("Word count is = {}", word_count);
}

Что мы здесь видим? Когда что-то внесено в стандартную библиотеку да ещё и автоматически импортируется, с этим бывает проще работать. В примерах выше у Rust таким лаконичным оказывается ввод-вывод, у Java — регулярные выражения, принимаемые как строковой аргумент.


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


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


В одном из альтернативных ответов предлагают использовать StringTokenizer API, что позволяет, наверно, надёжней считать слова: new StringTokenizer(str1).countTokens() однако не выглядит лаконичней, чем приведённый код. Такому сценарию можно сопоставить использование какой-нибудь библиотеки по работе с текстом и мне сложно представить, что её API для тех же задач будет сложнее.


Если посмотреть на другие варианты ответов — умение писать компактный код на Java — далеко не такой распространённый навык, как можно было бы ожидать.


Ну и последний момент — Rust создан для точного управления тем, что происходит в программе. Коммуникация — часть процесса управления. Иногда приходится сообщать больше, чтобы не было разночтений, и возможность что-то сообщить — упрощает написание качественного и согласованного кода. Компилятор может ловить достаточно сложные ошибки, за которыми в других языках пришлось бы бегать с отладчиком по всему проекту и молиться на QA, которые их заметят до релиза.


С другой стороны — в Rust реализован впоне здоровый компромис между детальностью описания и "догадливостью" компилятора. Нет необходимости уточнять вот прям всё-всё-всё — Rust умеет выводить типы, библиотеки предоставляют инструменты для конвертации типов. И есть неплохая система кодогенерации, позволяющая сокращать количество пользовательского кода на порядок, чем популярные библиотеки (и упомянутый в статье Exonum) достаточно часто пользуются.

Много текста слабо связанного с приведенным кодом, который показывает примерно ничего.

Ну вот вам в довесок для рассуждений еще (на таком аналитическом примере можно разных языков притащить).
{-# LANGUAGE OverloadedStrings #-}
module Main where

import Data.Text hiding (length)
import Prelude hiding (words)

main = do
    putStrLn "Simple Haskell Word Count Program"

    let str1 = "Today is Holdiay Day"

    let wordCount = (length . words . strip) str1
    putStrLn ("Word count is = " ++ show wordCount)

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

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

О комментаторе же, подробнее:

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

`println` — это не картина мира о вводе выводе. Даже не картина о выводе. Потому что под капотом забит конкретный кейс вывода в стдаут (в любом языке).

Rust принуждает обрабатывать ошибки — если мы пишем качественный код, им придётся уделять внимание в любом случае, но с Rust — все ошибки явные и их обработка входит в привычку.
И вот всплывают действительные различия на уровне семантики языков, а комментатор бодро их игнорит в примере.
Коммуникация — часть процесса управления.
О чем это вообще? Вроде ни акторы ни каналы не присутствовали в контексте.
Ну и так далее. Конкретика попыталась появится, но комментатор не ухватил.

Ок, давайте о комментаторе подробней, комментатор не против ;-)


Комментатор говорит за лаконичность кода и для примера используют внешнюю либу в своем примере.

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


При этом использованная внешняя либа — находится в так называемом "nursery", который отделён от стандартной либы очень тонкой (сходящей на нет) гранью. Это сравнивается с тем, что в Java аналогичная функциональность закопана в stdlib.


println! и иже с ним всякие write!, format! в Rust доступны программисту без дополнительных телодвижений (если он не делает странного), в Java аналогичные вещи нужно либо импортировать, либо вызывать полностью прописав весь путь к соответствующим классам и это тоже видно на примере кода.


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

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


О чем это вообще? Вроде ни акторы ни каналы не присутствовали в контексте.

То есть, на самом деле Вы просто не поняли о чём речь и из этого преждевременно сделали вывод, что комментатор говорил без конкретики, вместо того, чтобы прямо сообщить комментатору, что он как-то непонятно выразился?


Язык программирования — это таки язык — средство коммуникации, в данном случае человека с машиной. Акторы и каналы здесь ни при чём. Язык должен позволять человеку описывать то, что он хочет получить от машины.

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

println! и иже с ним всякие write!, format! в Rust доступны программисту без дополнительных телодвижений (если он не делает странного), в Java аналогичные вещи нужно либо импортировать
Т.е. притащить либу это ни о чем, а импорт целая катастрофа. Это я ёрничаю над объективностью оценки, на самом деле мне лично пофиг и на то и на то (ну кроме идиотского формата в принтлне с перечислением аргументов, вместо использования плейсхолдеров). В плане ио гораздо больше имеет значение работа с файлами. doc.rust-lang.org/1.22.1/std/fs/struct.File.html и что-то я опять не вижу особых различий по примеру, телодвижения ровно такие же как в любом другом языке. Можно рыть глубже и смотреть работу с буфферами для записи/чтения. Зато здесь есть куда более интересный момент с тем, что возвращаемые значения врапнуты (Result), что есть уже далеко не везде. Почему не сравнить это? Вполне практическая вещь, показывающая плюсы (можно спорить насколько жирный плюс, но уж не минус точно).

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

Вы напали на бредовый вброс про многословность раста, но сами при этом ответили не особо более качественно.

Пример был лишь конкретным ответом на вброс. Вы с этим ответом очевидно не согласны, но углубляться в детали чего-то более объективного не стремитесь. Ну ок.


Т.е. притащить либу это ни о чем, а импорт целая катастрофа. Это я ёрничаю над объективностью оценки.

Нападка была на многословность и именно она оценивалась, не что-то другое. При этом "притащить целую либу" — тоже было оценено: в случае Java эта "целая либа" — уже притащена в комплекте стандартной, а в случае Rust, хоть и оформлена отдельной либой, но по статусу близка к стандартной.


Точно так же упоминался и особый стиль работы Rust с ошибками. Но ок, пример с io тоже хорош.


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

Вам не кажется, что отрицать существование коммуникации "человек-машина", в качестве аргумента используя при этом язвительные эмоциональные ярлыки — несколько… гм… неконструктивно?

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


Плюньте. Это ж beduin01. У него все что не D-lang ( RIP праху его ) — то говно.
Вы хоть потрудитесь с синтаксисом D ознакомиться для начала. Вот вам пример seb.wilzba.ch/b/2018/02/the-expressive-c17-coding-challenge-in-d

D — 17 строк кода
Rust — 83 строки

Вопрос. Нафига в 4 раза больше кода писать? Вашу работу по количеству строк кода оценивают? Или для вас самоцель не решение задачи, а графоманство?
Вопрос. Нафига в 4 раза больше кода писать?


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

Спасибо, что практически «Hello, World» предлагаете рассмотреть в качестве примера. То, что вы привели — весьма полезно для студенческих тренировок, согласен.

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

Например, в сравнении мелких же примеров применения типа вашего Java тоже здорово сольет. А еще JVM долго стартует. И на этом основании можно сказать, что, к примеру, Perl лучше для «кровавого энтерпрайза» или для Android. Но ведь ПО для Android хорошо управляется Ява-машиной.

Это решение на Rust можно чуть ли не один в один переписать. Разница будет максимум в 10-15 строк (объявлять переменные и писать импорты в одну строку это конечно круто, но нет).

fn word_count(string: &str) -> usize {
    line.split_whitespace().count()
}

И действительно, какая же длинная функция получается.

Плюсы вот мейнстрим, а там намного внимательнее нужно быть. И плюс тулзы очень слабо развиты.
Но все равно очень много вещей на них пишут.
Лайфтаймы уж явно проще какого нибудь sfinae для восприятия. А главное раст реально не дает сделать множество видов ошибок просто по недосмотру

И плюс тулзы очень слабо развиты.

Вы об инструментах для С++, которых чуть ли не больше чем для всех прочих языков?

Я бы очень хотел, что бы у плюсов появился более-менее стандартный языковой пакетный менеджер, например. Пока к этому ближе всего https://conan.io, но даже он на практике ооочень далек от совершенства и тем более от общепринятости. Мало того что задача сложная из-за специфики языка и уже существующей инфраструктуры, еще и не так много кто понимает что это вообще нужная штука. :(

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

Другое дело, что кроме отсутствия явного лидера, есть еще и такой фактор: разработчики в массе своей уже приспособились и каждый коллектив научился решать эту проблему по-своему. Кто-то использует только системы пакетов из своих дистрибутивов. Кто-то применяет git submodules. Кто-то CMake-овские ExternalProject_Add. У кого-то свой велосипед.

Т.е. для большого количества C++ных команд эта проблема тем или иным образом уже решена. И пока нет явного лидера менять что-то работающее на что-то новое готовы не только лишь все :)

У Rust-а в этом плане все гораздо привлекательнее.

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

Я со своим уставом не лезу, боже упаси, просто интересно — не легче было просто в проект его положить? GTest очень легко прямо в проект на cmake вставить (там всего два исходника компилировать), и весит он всего ничего, да и внешних зависимостей меньше.

Какие именно низкоуровневые подробности Rust вынуждает указывать «ПОСТОЯННО»?
Например, строки против строковых слайсов.
Не очень понимаю, откуда такая резкая реакция, но с точки зрения прикладного программиста разница между &str, String и Cow имеет малое значение. И то, и то — «строки». Разница в основном в способе владения (и в изменяемости, конечно, но в прикладном коде строка после создания изменяется редко [1]).

Те, кто всё ещё не верят, предлагаю прочитать статью (ну или попробовать что-то большое написать, впрочем YMMV [2]).

Это проявляется везде. Конвертация String в &str (обычно достаточно &my_string, но в каких-то случаях — нет, надо делать .as_str()). Конвертация Option в Option<&str> (ну-ка, быстро, как это делается?). Конвертация &str и String в Cow (и обратно). Создание String из литералов (при том, что это ещё и лишнее копирование — но вроде оптимизация на подходе).

Возможно, конечно, мы просто сильно заморачиваемся и можно было бы просто фигачить String.

[1]. YMMV, в принципе, в Rust можно более безопасно изменять String благодаря строгому borrow checker-у.
[2]. Можно, например, вообще не заморачиваться и просто орудовать String, зависит, от задачи.

Про приведения типов — хз, я порядочно пишу на расте и по ощущениям большая часть преобразований это вообще просто & или .into().


Ну и кстати, компилятор или IDE частенько по делу подсказывают, если что :)

Тем не менее, это постоянно вылезает, о чём и был мой комментарий. Компилятор, конечно же, помогает, но это не убирает тот факт, что такие низкоуровневые моменты (потому что к бизнес логике они имеют слабое отношение) нужно указывать постоянно.

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

Я все равно не вижу в этом большей проблемы чем в, например, необходимости явно приводить i32 через as к f32 или usize, что уж я точно считаю плюсом языка в большинстве применений.

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

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

Что значит "надо признать"? Это же наоборот одно из достоинств языка — "явность", продуманная "синтаксическая соль" и все такое :)


хотя их количество и сокращают — например, RFC 2005

Эх, Я до сих пор с сомнением к этой инициативе отношусь — как бы не усложнили все этими упрощениями. Ну да посмотрим, может потом проникнусь все ж таки.

Ну и до кучи, маленькая задачка. Хочу иметь ключ в хэш-таблице из пары строк. Ключ хранит строки (т.е использует String). При поиске у меня есть пара строковый слайсов.

Поехали, три варианта, выбирайте на свой вкус (предлагайте свой, лучше!):

use std::mem;
use std::borrow::{Borrow, Cow};
use std::collections::HashSet;
use std::hash::{Hash, Hasher};

fn size_of_key<T>(_set: &HashSet<T>) -> usize {
  mem::size_of::<T>()
}

fn main() {
  // Extra string copying on lookups :(
  let mut set = HashSet::new();
  set.insert(("hello".to_string(), "bye".to_string()));
  let flag = set.contains(&("hello".to_string(), "bye".to_string()));
  println!("{}, key size {}", flag, size_of_key(&set));


  // Extra memory used for Cow :(
  let mut set = HashSet::new();
  set.insert((Cow::Owned("hello".to_string()), Cow::Owned("bye".to_string())));
  let flag = set.contains(&(Cow::Borrowed("hello"), Cow::Borrowed("bye")));
  println!("{}, key size {}", flag, size_of_key(&set));

  // Whaaat? BUT:
  // No string copying! Key is small! Dynamic dispatch, though :(
  #[derive(PartialEq, Eq, Hash)]
  struct OwnedPair(String, String);

  #[derive(PartialEq, Eq, Hash)]
  struct BorrowedPair<'a, 'b>(&'a str, &'b str);

  trait KeyPair {
    fn pair(&self) -> (&str, &str);
  }

  impl KeyPair for OwnedPair {
    fn pair(&self) -> (&str, &str) {
      (&self.0, &self.1)
    }
  }

  impl<'a, 'b> KeyPair for BorrowedPair<'a, 'b> {
    fn pair(&self) -> (&str, &str) {
      (self.0, self.1)
    }
  }

  impl<'a> Borrow<KeyPair + 'a> for OwnedPair {
    fn borrow(&self) -> &(KeyPair + 'a) {
      self
    }
  }

  impl<'a> Hash for (KeyPair + 'a) {
    fn hash<H: Hasher>(&self, state: &mut H) {
      self.pair().hash(state);
    }
  }

  impl<'a> PartialEq for (KeyPair + 'a) {
    fn eq(&self, other: &Self) -> bool {
      self.pair() == other.pair()
    }
  }

  impl<'a> Eq for (KeyPair + 'a) {}

  let mut set = HashSet::new();
  set.insert(OwnedPair("hello".to_string(), "bye".to_string()));
  let flag = set.contains(&BorrowedPair("hello", "bye") as &KeyPair);
  println!("{}, key size {}", flag, size_of_key(&set));
}

Ну такое, довольно специфическая задача, кмк. Да и первый вариант, если мы не хотим лезть в "низкоуровневые детали" чем плох? Да, выделения памяти, но мы же хотим высокоуровнево, значит нам не особо и важно.


В целом про "String vs &str" — мне это вообще не видится "низкоуровневой деталью", это часть общего фокуса ржавчины со строгим отслеживанием владения и в моей голове оно все скорее как раз высокоуровневая штука про организацию программы и поток данных, а не про байтики в куче и стеке. И все это разделение на значения, изменяемые и неизменяемые ссылки в целом делает программу более поддерживаемой и понимаемой (при правильном применении).

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

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

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

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


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

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


В общем, нет, я не считаю разделение значений, изменяющих и неизменяющих ссылок низкоуровневой деталью, а String(~str)/&str разделение является просто одним из продолжений этого подхода.

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

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

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

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

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

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

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

Тоже не особо редкий случай, в общем-то.

HashMap<String, HashSet<String>> не пробовали использовать? Дописки для удобной работы с таким типом не слишком большие: playground.


Если без HashSet<(String, String)> совсем никак и хочется выжать последние байты и циклы работы процессора, то можно залезть и в unsafe. Предоставив безопасный интерфейс, конечно. Очень страшный playground

Всё было бы гораздо проще, если бы std::borrow::Borrow borrow() возвращал бы Borrowed, а не &Borrowed.

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

Просто человек вот удивлялся, когда это дескать в Rust низкоуровневые вещи постоянно приходится указывать — я говорю, что постоянно, особенно со строками. Я считаю, что с точки зрения логики прикладной программы все эти жонглирования особо смысла не имеют. С точки зрения корректности программы на Rust — разумеется, с точки зрения производительности — да, конечно.
Вообще, изначально мой вопрос был не вам. Для меня такая работа со строками не является «низкоуровневой», а разобравшись с принципами владения, я начал иначе писать код на других языках и считаю это безусловным плюсом. Кажется, что большинство возмущений по поводу Rust является результатом «парадокса языка блабл», а следовательно банальным невежеством c которым следует бороться.
Некоторые не хотят потом 10 лет править сегфолты в коде. И для них экономически поравдано писать на расте. Если же у вас ХХивП, то вам конечно раст не нужен.
Ну и потом у некоторых говнокодеров реально бомбит, когда компилятор умнее их.
Базы данных, ядра ОС написаны на Си. Как часто встречается в них сегфолт? Наверное дело не в языке, а в людях. И в rust есть null, а значит «классика»(обращение к null) никуда не уходит.

Проектов на си, где ничего не падает можно пересчитать на пальцах одной руки. Ядро linux вних не входит, если что.

null в Rust есть только на уровне небезопасных абстракций ("сырые" указатели и им подобное). Да, код, работающий с ними напрямую, должен об этом беспокоиться — но это в худшем случае несколько абстракций, предоставляющих внешнему коду безопасный интрефейс, а не весь объём кода приложения.

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

В servo есть минимум одна большая и сложная абстракция (интеграция с JavaScript-движком mozjs, вариантом SpiderMonkey, который написан на C++), плюс логические ошибки, которые ловят ручные или автоматические assert-ы разных видов, в некоторых случаях недостаточно оптимизированное потребление ресурсов на особо весёлых страницах (например, slither, создающий более 2000 canvas-ов). Иногда ещё драйверы GPU вылетают.

Как бы там не только проблема в JS. Особенно доставляет комментарии типа


I have not been able to reproduce this in a local debug build following those steps.

Все точно так же как на C++

Однако это капля в море разрабатываемого софта.


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

Обидно что при этом как раз на embedded они при этом и не ориентируются — во всяком случае для контроллеров тулчейн вроде бы ещё в детском состоянии и обложен third-party костылями разной степени допиленности, и создаётся впечатление, что для разработчиков это не приоритет.

Вы не правы. Очень даже приоритет.
Embedded devices. A domain with a great deal of potential that is not yet first-class.
While we cannot hope to stabilize everything needed for embedded development in 2018, we can step up our game and begin to treat the market as more mainstream.

То есть намерение начать трактовать embedded как равноправную область есть, но в 2018 году надежды на что-то стабильное ещё нет. За намерение тоже спасибо, конечно, раньше и этого не было. Но пока очень расплывчато и без ясных планов, так что ещё на год в долгий ящик, во всяком случае с прицелом что-то в боевых условиях попробовать.

У них есть беда: rust пока что живёт поверх clang, а не GCC, а для clang'а embedded — не приоритет.

Так что либо менять направление развития clang'а, либо переходить на GCC. И то и другое — мягко говоря, непросто.

Rust живёт поверх LLVM, и это совсем не одно и то же, что и Clang.

Я бы сказал что это «не совсем» то же, что clang. Говорить всё-таки, что это «совсем не одно и то же» нельзя — они слишком тесно связаны. Но да, извиняюсь, моя ошибка.
но в 2018 году надежды на что-то стабильное ещё нет.

Сам Rust как язык еще только-только стабилизировался.
А вы уже хотите стабильность по платформам.

Ну, 1.0 вроде в мае 2015 появился, без малого три года уже. И я скорее хочу чтобы они с позиционированием определились — язык вроде как системный, но в сторону embedded (где он был бы прямо подарком) не смотрит, а смотрит, судя по статьям, в сторону, например, бэкенда — уже прочно оккупированного языками со сборкой мусора.

Если вы действительно хотите получить представление о положении дел в embedded, то могу посоветовать вот этот пост.

Плюс блог небезызвестного japaric, который стоит на острие прогресса в этой области.

И еще момент: не надо воспринимать авторов языка, как разработчиков проприетарного софта. Да, многие работают в Мозилле и получают за это деньги. Но развитие языка во многом определяется именно сообществом.

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

Таким образом, язык такой, каким его хочет видеть активная часть сообщества. И только.

Спасибо за ссылку, хороший доклад. И, да, мне действительно интересно, в том числе с практической точки зрения. Я уже пробовал нырнуть в embedded rust, но выныривал с ощущением, что пока всё хрупко, неофициально и в постоянном движении.


И я понимаю, конечно, что разработчики и сообщество никому ничего не должны. Но и потенциальных разработчиков на Rust не стоит лишать права голоса. Активная часть сообщества — это не только те, кто послал туда патч. Это ещё и те, кто будет его использовать как только он будет более или менее годен к применению в их области (и тогда, возможно, посылать патчи, делать библиотеки, и прочая и прочая). А пока не могут, высказывают своё огорчение, вот как я сейчас. :)

Но и потенциальных разработчиков на Rust не стоит лишать права голоса.

Именно поэтому была выдвинута инициатива #Rust2018, в рамках которой сообщество писало посты со своим видением ориентиров развития на ближайший год со своими пожеланиями и обозначением собственных проблем.

Было написано больше сотни постов, которые потом анализировались и упомянутый выше roadmap как раз был основан на компиляции озвученных идей.

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


Это плохо.
Сообщество желает видеть язык для своей цели.
А целей у людей в сообществе — не счесть.
Тянуть язык в разные стороны — это не гуд.
Надесь, что вы все же заблуждаетесь насчет роли сообщества.
Не путайте, пожалуйста, демократию и анархию. Если вам интересно, как все устроено, почитайте про процесс RFC и рабочие группы языка.
Не путайте, пожалуйста, демократию и анархию.


Что вы, я никак не подозревал сообщество Rust в анархии. Это попросту невозможно в пределах одного языка. Вопрос о стройности, единообразии. Это проблемы любого достаточно большого сообщества. У демократии есть недостатки, органически связанные с ее достоинствами. Когда отвечают за систему «все», — значит отвечает «никто». На самом деле система, конечно же, продолжит работать, потому как все заинтересованы в том, чтобы она работала. Но вот отдельные куски — могут быть даже противоречивы. Когда нет в языкостроительстве «тирана» — стройности языка достичь трудно, если вообще возможно. Как пример, наше обсуждение языков тут в комментариях.

Роль тирана в принятии или убийстве RFC выполняет команда ядра — https://www.rust-lang.org/en-US/team.html — все под контролем :)

Как по заказу, специально для вас :) Анонс Embedded Devices Working Group.
Спасибо. Очень хороший обзор, особенно раннего этапа (я про него вообще не знал).
Можно ссылку на «самый популярный ЯП по данный SO»? Это в какой-то подкатегории? Например среди ЯП с названием начинающимся на «Ru» и заканчивающимся на «st»?

Вероятно, имеется в виду Stackoverflow developer survey, в котором уже второй год подряд Rust занимает первую строчку в номинации Most loved language. Согласен, что перевод несколько некорректен.

Может, я не прав, но loved != популярный

Не иначе как по тэгу «Какого черта?!»

Так вот она какая каша из топора...

Rust используется и в Dropbox — на этом ЯП написано ядро их продукта.


это для меня стало новостью, летом видел здесь статью о том, что dropbox переписали на Go =) про Rust было ни слова…

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

Можно ли попросить пруфов для этой истории?

Ибо когда информация об использовании Rust в Magic Pocket появилась пару лет назад, то речь шла о том, что Dropbox уходил с Python-а на Go и написал свою Magic Pocket на Go. Очень сильно выиграв при этом и в производительности, и в ресурсоемкости. И только в одном месте обнаружилась проблема с предсказуемостью поведения кода на Go (из-за GC). Это место переписали на Rust-е. И в итоге вышло что-то порядка 30 или 60 KLOC кода на Rust-е в продукте объемом 1.3 MLOC на Go.

За прошедшие пару лет что-то изменилось?

Вот видео в митапа, в котором про то что dropbox делает. Потом та же команда искала rust разработчика


https://air.mozilla.org/rust-meetup-may-2017/

Для меня английский сильно не родной, но разве где-то в районе 7:35-7:45 докладчик не говорит что-то вроде: «Magic Pocket был изначально написан на Go. И сейчас он все еще большей частью на Go. Может быть 30-40% на Rust-е»?
Для меня английский сильно не родной, но разве где-то в районе 7:35-7:45 докладчик не говорит что-то вроде: «Magic Pocket был изначально написан на Go. И сейчас он все еще большей частью на Go. Может быть 30-40% на Rust-е»?


Это быть не может физически.
Они с Python на Go переходили несколько лет, там большой объем.
Первые эксперименты с Rust начались 2 лет назад.
К настоящему времени не может быть 40% даже если они и решили мигрировать на Rust

https://news.ycombinator.com/item?id=11283758


Actually, full disclosure, we really just rewrote a couple of components in Rust.

То есть, по их собственным словам, пару компонентов переписали — остальное все ещё на Go.

Я-то это понимаю, но в статье сказано буквально «Поэтому хранилище переписали на Rust, и проблема с памятью решила». Отсюда и первоначальный комментарий.

На счет первых экспериментов. В 2016-ом была информация о том, что код на Rust уже работает в Magic Packet. Все это обсуждалось в двух местах:
news.ycombinator.com/item?id=11282948
www.reddit.com/r/rust/comments/4adabk/the_epic_story_of_dropboxs_exodus_from_the_amazon
В разных комментариях этих обсуждений.
На счет первых экспериментов. В 2016-ом была информация о том, что код на Rust уже работает в Magic Packet.

Да, как раз 2 года назад, в 2016 и были эти первые эксперименты. О них и речь.
Эксперименты были полноценными, с внедрением в production, да. И успешными, да. Но с тех пор, как видим, необходимости что-то переписывать на Rust, кроме пары этих критичных компонент и нет.

Простите мне мою дотошность, но если в 2016-ом году было заявлено об успешном использовании Rust-а в продакшене, то эксперименты должны были начаться сильно раньше. С учетом того, что Rust использовала совсем небольшая команда и было написано 60KLOC, то процесс этот стартовал уж не как не позже 2015-го года.
Но с тех пор, как видим, необходимости что-то переписывать на Rust, кроме пары этих критичных компонент и нет.
Ну вот как раз никто еще не смог толком сказать о том, что же было «с тех пор». Может там серьезное переписывание MagicPocket с Go на Rust идет.
Может там серьезное переписывание MagicPocket с Go на Rust идет

Наш человек в Дропбоксе Slava Bakhmutov ( Dropbox SRE, twitter.com/M0sth8 ) утверждает, что так и есть до сих пор: буквально пара самых высоконагруженных мааааленьких компонентов на Rust, большая часть на Go и небольшие остатки от предыдущей реинкарнации Дропбокса на Python.
Rust используется и в Dropbox — на этом ЯП написано ядро их продукта.


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

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

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

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

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

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

Как-то так.
Зарегистрируйтесь на Хабре , чтобы оставить комментарий