Pull to refresh

Comments 98

У Rust выявили «фатальный недостаток»?
UFO just landed and posted this here
Ну, если вы помните все перипетии и нюансы взаимодействия Pin/Unpin/!Unpin, когда что выводится, и когда надо потребовать анпин, а когда надо наоборот запретить анпин (двойное отрицание, привет!).

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

А как это относится к эффективной компиляции? Лямбды в Расте имеют разные типы по той же причине, что и в С++. Лямбда — это просто синтаксический сахар для анонимной структуры с соответствующим методом. Две лямбды — две разные структуры, соответственно тип у них разный.

Какую реализацию вы предлагаете как более удобную? С учетом того что язык компилируемый, нативный и с минимальным рантаймом.
А как это относится к эффективной компиляции? Лямбды в Расте имеют разные типы по той же причине, что и в С++. Лямбда — это просто синтаксический сахар для анонимной структуры с соответствующим методом. Две лямбды — две разные структуры, соответственно тип у них разный.

Ну можно было бы боксить по-дефолту и проводить эскейп анализ. Да, звучит сложно, но зато при необходимости связать пару функциональных параметров не пришлось бы городить монстров вроде такого: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=3c0bec29d4f38f8eedae7a42446f0480


Для сравнения. вот так этот же код выглядит на скале:


def stateMonad[S]: Monad[State[S, ?]] = {
 type StateS[A] = State[S, A]

 new Monad[StateS] {
  override def mreturn[A](a: => A): StateS[A] = State(s => (a, s))

  override def bind[A, B](f: StateS[A])(g: A => StateS[B]): StateS[B] =
   State[S, B](
     s1 => {
       val (a, s2) = f.run(s1)
       g(a).run(s2)
     }
   )
 }
}
UFO just landed and posted this here

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


Или это такой тонкий сарказм что я не распознал.

Искренне не понимаю людей, которых отталкивает чужеродность синтаксиса Rust. Его и так уже где только можно привели к C/C++, иногда даже в ущерб логике.

Просто очередное изнасилование журналиста ученым (как всегда, ради громкого заголовка):


Hi Ryan Levick here. Microsoft is not moving away from Rust. Verona is a research project to explore different memory safety models for systems programming. Verona is in part inspired by Rust (as well as pony and C#). It is not a fork of Rust or meant as a Rust replacement. It’s very early days in Verona’s development and hard to say what it’s future will be. The hope is to learn from Rust and if possible contribute back learnings into the language. This isn’t a zero sum game. The talk linked to in the CDNet article should hopefully clarify things. That article was very misleading.

отсюда

Так то весь смысл статьи меняется
А то я уже хотел было писать, что, мол жаль Майки не вложатся в Раст, чтобы сделать еще более крутой язык
Репозиторий проекта уже опубликован на Github.

Кхм. Но там ничего нет кроме файла лицензии.

Сначала был Rust, который умел исключать ошибки при работе с памятью, но в это время группа сотрудников Microsoft обнаружила в Rust фатальный недостаток — его писали не они! Они немедленно исправили этот недочет, создав Verona, который как Rust, но другой.

UFO just landed and posted this here
Как указывает ZDnet со ссылкой на Мэтта Миллера, специалиста Microsoft по безопасности, около 70% всех уязвимостей, которые были обнаружены в продуктах Microsoft в последние годы, были связаны с ошибками управления памятью.


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

〈/sarcasm〉
Кстати, очень любопытно окунуться в историю и посмотреть каким раст планировался в 2009-2010 годах (тыц). Видно, что это просто попытка избавиться от основных проблем С++, оставив всё остальное как есть.
UFO just landed and posted this here

А в чем собственно проблема с <>? Пример абсолютно нормальный, поэтому непонятно, что хотели им показать.

Если этот пример нормальный для раста (а не специально сделанное извращение), то это явно довод против него (с раст знаком шапочно, перевести не смог)

UFO just landed and posted this here

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

Ну вот так примерно будет выглядит в сишарпе:


class Reference<LifetimeA, Path> : PartialEq<Cow<LifetimeB, OsStr>> { ... }

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


class Rc<TMaybeUninit> where TMaybeUninit : MaybeUninit<T>[] { ... } 

Вся разница с шарповым кодом в том, что апострофы для лайфтаймов используются, да & вместо Reference. Вас так апострофы напугали?

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

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

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


Если не понимать, что такое лайфтаймы, трейты и т.п., то ничем не поможет и такая запись


declare lifetimes a, b
implement trait PartialEquality<CloneOnWrite(b)<OsCompatibleStringSlice>>
for reference(a)<Path>
я этот синтаксис хотя бы загуглить могу, могу поискать, что такое lifetimes, trait и т.д, если возникнет такая необходимость. Как гуглить конструкции вида
impl<'a, 'b> PartialEq<Cow<'b, OsStr>> for &'a Path
не очень понятно)

Это понятно если прочитать учебник по расту. На который емнип я потратил часов 6 чтобы прочитать от корки до корки.


Синтаксис учится один раз, а потом используется многократно. Поэтому вариант который взял раст на мой взгляд намного читаемее чем то что написано выше. Если в случае с


impl<'a, 'b> PartialEq<Cow<'b, OsStr>> for &'a Path

глаз сразу выцепляет "какие времена жизни, у кого, для кого", то в джава-стайл варианте выше глаз об стену текста разбивается

Как показывает практика, большой процент вопросов просто гуглится.

Вы обычно гуглите базовые понятия языка на котором ведете разработку? Вроде "что такое class" или "что значит null"?

Нет конечно. А к чему вопрос?

прошу прощения, хабрауведомления напутали получателя.

Ох, не хочется произносить нецензурных слов, но система комментариев и уведомлений на Хабре — та ещё :)

Ох. Работал я в одном проекте с бэкендом на Scala, одним из главных локальных мемов был исходник одной из основных подсистем строк в 300. Первые 120 и последняя сотня просты и предельно понятны, но вот 80 строк собственно тела сервиса с первых трёх сворачивают мозг в межушный нервный узел, оставляя только 4 символа W, T,? и F в мыслях.Самое страшное — то, что этот код отлично справлялся с поставленными задачами с отличной производительностью, т.к. был в важной и нагруженной части проекта, но для внесения любых изменений команда «теряла» разработчика полностью на несколько часов.
UFO just landed and posted this here
Пару раз при мне пытались, провисали по производительности или corner case-ам из боевого опыта.
Да, забыл отметить, текст был предельно читаемым (т.е. максимально далёк от программ ACM-щиков, не в обиду), но ему это не помогало.
Для меня это выглядит как Карго с новомодных шаблонов C++
= вместо того, чтобы от этого ужаса избавиться, в Rust — почему-то решили скарго-культить это извращение. :(
image
А какая альтернатива шаблонам? Вынести типы как обычные параметры, как сделано в языках с зависимыми типами?

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

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


fn f(x: &i32) -> impl Fn() -> i32 + '_ {
    move || *x
}

fn main() {
    let y;
    {
        let x = 5;
        y = f(&x);
        println!("{}", y());
    }
    println!("{}", y());
}

С первым println всё хорошо, а вот со вторым — нет:


error[E0597]: `x` does not live long enough
  --> src/main.rs:9:15
   |
9  |         y = f(&x);
   |               ^^ borrowed value does not live long enough
10 |         println!("{}", y());
11 |     }
   |     - `x` dropped here while still borrowed
12 |     println!("{}", y());
   |                    - borrow later used here

Поэтому сравнивать синтаксис с языками которые предоставляют меньше информации (например, в языке с ГЦ такое соответствие времен жизни указать нельзя, да и смысла там в этом мало) я считаю некорректно.

А что, в других языках есть что-то более удобное для указания типовых параметров кроме <>? В вышеприведенной доке для этого другие скобочки [], но смысл-то от этого не меняется.

Ну да, конструкции типа
Arc<Mutex<Receiver<Option<Message<T>>>>>


Довольно многословны, но какие альтернативы? Разве что сделать какой-то разделитель одним символом, а не скобочками, типа
Arc!Mutex!Receiver!Option!Message!T

Но тогда как записывать, если типовых параметров больше одного? Какой синтаксис для лайфтаймов будет более удобен?
Довольно многословны, но какие альтернативы?

type Mailbox<T> = Arc<Mutex<Receiver<Option<Message<T>>>>>;

И теперь всего одна пара скобочек.

можно вывести категорию «функции (шаблоны) с одним параметром-типом на входе и одним типом на выходе» как «модификаторы типа» и записывать вообще без спец символов — Arc Mutex Receiver Option Message T. Что-то вроде const в c++, но с адекватными правилами записи/чтения.

Напоминает каррирование :3

У меня единственное возражение против угловых скобок — это не скобки! Поэтому редакторы очень часто не могут подсветить соответствующую закрывающую/открывающую угловую скобку.


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


type Mailbox[T] = Arc[Mutex[Receiver[Option[Message[T]]]]];

type Mailbox<T> = Arc<Mutex<Receiver<Option<Message<T>>>>>;
Точки лучше:
type Mailbox<T> = Arc.Mutex.Receiver.Option.Message<T>

Или так:
type Mailbox<T> = Arc*Mutex*Receiver*Option*Message<T>

Соглашусь, но так не видно вложенность. Нужна ли она — это другой вопрос.

Предлагаете отказаться от использования символа * для указателя и операции разыменования? Что тогда вместо него?
В Rust можно так:


<MyType as FooTrait>::foo()

foo::<A, B>(a, b)

Поставите тут точку — и у вас развалится парсинг statements, потому что точка традиционно используется для обращения к полям и методам.

Согласен, точка и звездочка уже под другое зарезервированы. Тогда такие варианты:
type Mailbox<T> = Arc^Mutex^Receiver^Option^Message<T>
type Mailbox<T> = Arc|Mutex|Receiver|Option|Message<T>
type Mailbox<T> = Arc+Mutex+Receiver+Option+Message<T>
type Mailbox<T> = Arc~Mutex~Receiver~Option~Message<T>
type Mailbox<T> = Arc Mutex Receiver Option Message<T>
type Mailbox<T> = Arc,Mutex,Receiver,Option,Message<T>

Я Раст не знаю, там что-то из этого свободно?
Последний вариант самый приятный и интуитивно понятный.

Почему у вас вдруг Option+Message<T>? два синтаксиса для обозначения одного и того же. Тем более, что угловые скобки во всех популярных языках используются для генериков/шаблонов.


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


В текущем виде грамматика раста практически контекстно-независима.

А как тогда записывать такое?


Rc<[MaybeUninit<T>]>

Или массивы и срезы уже не считаем за типы?

Я просто приводил пример, что квадратные скобки "выглядят лучше".
Разумеется, если квадратные скобки отдать под дженерики, то массивы и срезы надо записывать как-то иначе.


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


Разумеется, менять синтаксис так кардинально в Rust уже поздновато.

Круглые скобки в Rust используются для типов-кортежей.

У скалы и раста совершенно разное предназначение. И как ни странно, синтаксис это должен отражать.

Там NLL в этом году завезли, половину лайфтаймов можно не писать.

NLL к лайтаймам в сигнатурах отношнеия не имеет.


А вот возможность использовать анонимный лайтайм '_ часто выручает

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

NLL как следует из названия меняет работу лайфтаймов с лексических блоков на нелексические.


А блоки встречаются только в теле функций. В определениях их нет.

не бывает ошибок с памятью

Почитайте блог PVS Studio

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

〈/sarcasm〉

Это вобщем-то и без сарказма почти верно.
Просто "настоящие программисты" (в хорошем смысле) бизнесменам не нужны. Поэтому даже те, кто мог бы быть ими, обычно вынуждены играть роль плохих.

Даже настоящим программистам ни к чему делать лишнюю работу, которую можно переложить на компилятор. Или «настоящие программисты» кроме ассемблера ни чего не признают?

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


А вот с управлением памятью ситуация совершенно другая. Автоматическое управление памятью всегда создаёт оверхед, и работает хуже чем это может сделать программист вручную. И вряд ли ситуация сильно изменится если только этим не займётся AI. Ну, насчёт оверхеда есть одно почти исключение — c++ управление временем жизни объектов (не указателей) при должном уровне компиляторных оптимизаций и при качественной реализации конструкторов копирования/перемещения, операторов присваивания (всё это с полноценным учётом const/nonconst аргументов) и деструкторов этих самых объектов, на современных компиляторах может быть с довольно маленьким оверхедом. Однако обычно это даже не считают автоматическим управлением памятью.

Rust позволяет управлять памятью практически так же точно, как и C/C++, только контролируя безопастность. Сам он удаляет объекты примерто так же, как C++ со смартпоинтерами, зачастую с меньшим оверхедом. Даже если хочется странного, типа иметь несколько указателей на объект и удалать его по любому из них, можно все это сделать через unsafe и сырые указатели.

Вы то ли подменяете понятия то ли не понимаете сути. Я не знаю что и как в Rust, но ваши заявления касательно C/C++ странные.


Во-первых, "как C только контролируя безопасность" это и есть оверхед. В glibc например если поставить переменную окружения _MALLOC_CHECK=2 он тоже будет что-то контролировать, но это не продакшн функция а отладочная. Это если про само выделение/освобождение. Если же речь про невыход за границы выделенной памяти то это ещё больше оверхеда (при каждом обращении к массиву сверять индекс с чем-то в памяти, а уж как в таком режиме работать с type-casted указателями я вообще не знаю).


Во-вторых, когда я выше писал про c++ это было не про smart pointers а про RAII. Сам RAII добавляет немного (немного — повторюсь — на современных компиляторах и с качественной реализацией класса) оверхеда, а smart pointers с refcount — это абстракция над RAII которая добавляет ещё оверхеда. Если речь про без refcount то это тоже самое что статический объект. Случай с малым оверхедом это когда функционал smart pointer'а встроен в используемые классы, но не отдельной абстракцией а монолитно.


Иметь несколько указателей на объект это штатная фича указателей. А ещё бывают указатели на не-объект, например на позицию "+2 байта от начала переменной long long x;" (тип long long обычно 64-битный). То есть указатель это совсем-совсем не обязательно результат работы аллокатора для какого-то класса.


можно все это сделать через unsafe и сырые указатели.

То есть если хотим максимальной эффективности — возвращаемся в тому что было.

Во-первых, "как C только контролируя безопасность" это и есть оверхед. В glibc например если поставить переменную окружения _MALLOC_CHECK=2 он тоже будет что-то контролировать, но это не продакшн функция а отладочная. Это если про само выделение/освобождение. Если же речь про невыход за границы выделенной памяти то это ещё больше оверхеда (при каждом обращении к массиву сверять индекс с чем-то в памяти, а уж как в таком режиме работать с type-casted указателями я вообще не знаю).

Почти все проверки раст делает во время компиляции, и рантайм проверки можно тоже выключить. Например, компилятор раста автоматически проверит, что у вас ссылки не алиасятся, и на все указатели/ссылки в прогармме повесит __restrict. Надо ли говорить, что это не замедляет, а ускоряет программу?


Во-вторых, когда я выше писал про c++ это было не про smart pointers а про RAII. Сам RAII добавляет немного (немного — повторюсь — на современных компиляторах и с качественной реализацией класса) оверхеда, а smart pointers с refcount — это абстракция над RAII которая добавляет ещё оверхеда. Если речь про без refcount то это тоже самое что статический объект. Случай с малым оверхедом это когда функционал smart pointer'а встроен в используемые классы, но не отдельной абстракцией а монолитно.

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


То есть если хотим максимальной эффективности — возвращаемся в тому что было.

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

концептуально в раст тот же плюсовый RAII, а безопасность обеспечивается в компайл-тайме. Реальный оверхед может быть на проверках там, где компилятор ожидаемо не может проследить гарантии, предоставленные вызывающим методом.
Иметь несколько указателей на объект это штатная фича указателей. А ещё бывают указатели на не-объект, например на позицию "+2 байта от начала переменной long long x;" (тип long long обычно 64-битный). То есть указатель это совсем-совсем не обязательно результат работы аллокатора для какого-то класса.
для таких случаев лучше иметь ссылку и смещение перенести поближе к использованию. А еще лучше вообще не складывать байты в целочисленные типы
то, что уязвимости были обнаружены «в последние годы», не значит, что они не существовали с 80х.
И почему до сих пор ABI Rust нестабилен, как мне кажется это ключевая фишка, чтобы потеснить C++. Crates это конечно хорошо, только когда мы собираем монолитный бинарник. Каким образом Майкрософт будет осуществлять разработку системных компонентов? Она по большей части предполагает наличие динамических библиотек, а в Rust стабильность ABI можно гарантировать только на уровне определённой версии rustc. Они, конечно, могут ограничиться только написанием обособленных компонентов вроде драйверов, но это сильно занижает потенциал Rust.

Совместимость с C поддеживается, а нативный растовый ABI не слишком нужен в условияк сборки через cargo.
Но пока слишком велик риск допустить архитектурную ошибку. Я надеюсь, разработчики запилят GATы, а сейчас активно пилятся const-generics и другие фичи, и заморозить ABI значит обратить всё в legacy. Я лучше буду мучиться с перекомпиляцией пока.

А может кто подробнее объяснить, почему это нет в Rust (и нет ли)?

Основным отличием Verona от Rust является применение модели владения на основе групп объектов, а не единичных объектов. Данные в Verona рассматриваются как структуры, представляющие собой коллекции объектов.

Для этого неплохо бы понять для начала, как выглядит эта модель владения "на основе групп объектов".

Да, и это тоже, я как-то потерял эту часть фразы.
Вероятнее всего «фатальный недостаток».
Но по мне так конкуренция — хорошо. Может кто-нибудь наконец то HKT реализует.
А чем .NET плох? Вроде, платформа стабильная, а так же есть ручное управление памятью.
Не понимаю смысла городить новые языки.
Ой, имел ввиду автоматическое управление памятью.
GC для некоторых задач не слишком эффективен.
У .net другая ниша. Тяжело на дотнете что-то системное пилить.
Ну фиг его знает. А что мешает доработать .NET под это все дело?
Скажем, добавить новый синтаксис для взаимодействия с неуправляемым кодом и все такое.
Тогда получится C++/CLI, самый уродливый язык, когда-либо созданный для практических целей.
Либо ещё один Rust.
Ну… они могли бы поучиться над ошибками и сделать красивый синтаксис.
Мне кажется, именно этим сабжевый отдел Microsoft и занимается.

Синтаксис C# действительно лаконичнее и читабельнее, но всё-таки zero-cost abstractions — это отдельная парадигма, под которую шарп изначально не затачивался, а если насильно натягивать язык на новую парадигму, мы получим ещё один C++ со всеми его проблемами.
Да, я согласен.

Однако, надеюсь, что дадут какой-нибудь способ взаимодействовать с ним из .NET.

Главный вопрос — а зачем?

Так синтаксис итак есть. И CoreRT для компиляции в нативный код разрабатывают. Но Майкрософт виднее

У дотнета есть рантайм — сборщик мусора и JIT, чего хочется избежать при разработке ОС, ну и памяти оно жрёт сильно больше, а ручное управление памятью сильно ограничено

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

UFO just landed and posted this here

ЕГО ПИСАЛИ НЕ ОНИ! (,)


опять "давай сделаем такой же *, только лучше, но другой".


Хотя цель благая вроде.
Им наконец надоело стрелять себе в ногу всякими там memset'aми.


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

И ещё как.

А вот какой был ответ от сотрудника майкрософт:
«Hi Ryan Levick here. Microsoft is not moving away from Rust. Verona is a research project to explore different memory safety models for systems programming. Verona is in part inspired by Rust (as well as pony and C#). It is not a fork of Rust or meant as a Rust replacement. It’s very early days in Verona’s development and hard to say what it’s future will be. The hope is to learn from Rust and if possible contribute back learnings into the language. This isn’t a zero sum game. The talk linked to in the CDNet article should hopefully clarify things. That article was very misleading. (https://www.reddit.com/r/rust/comments/e5kjyr/more_info_on_micrsoft_moving_away_from_rust/)»
Sign up to leave a comment.

Other news