LINUX.ORG.RU

Посмотрел я этот ваш Rust

 ,


6

8

Вобщем, дошли руки потыкать палочкой.

Я вот что не пойму - зачем и кому он нужен, ну правда?

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

Close to metal? Нет, извините, мне когда надо будет close to metal - я пойду сишку возьму. Которая реально, и Close To Metal, и со стабильным ABI, так важным для низкоуровневого программирования, и так далее. А если просто производительности не будет хватать, в том числе там из-за GC, так ведь - что в Java, что в Common Lisp, есть огромное количество возможностей затюнить производительность до нужного уровня, при этом не стреляя себе в ногу.

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

Наконец, ну безопасность чтоли, хваленая? Ну, опять нет. Взять тот же unsafe. Если вам нужна прямо таки безопасность-безопасность - берите что-нибудь вроде хаскеля(или какого-нибудь Coq, или что-нибудь подобное, с зависимыми типами, если совсем упоролись), ну или на худой конец, что-нибудь вроде Java, где все безопасно прямо как в дурдоме с мягкими стенами.

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

★★

Ответ на: комментарий от byko3y

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

Cache-miss важен для научных последовательных расчётов. Для последовательной обработки данных.

Искусственный интеллект(логика принятия решений) не сильно коррелирует с кэшем. С середины 80-х началось ускорение увеличения разрыва между CPU и памятью. После 2010 почти остановилось, стали увеличивать SRAM cache.
Если смотреть на игры 90-х и начала 2000-х, то в ИИ плотность принятия решений не сильно изменилась. Иногда кажется что ИИ ухудшился потому что программистов отвечающих за это не осталось в компаниях, или не умеют. Основная нагрузка на тех кто рендерит и отдаёт модели программерам для занесения в купленный готовый движок. Теперь капли от дождя на плаще, а игра тупая.
В играх открытого мира иммитация, которую долго упрощают и отлаживают.

А теперь об экономике, предположения.

1 бит в SRAM это 6 CMOS(КМОП) транзисторов.
1 бит в DRAM это 1 транзистор и конденсатор.
Конденсатор это инерция. Но этот конденсатор как раз и является трудным компонентом.
Если SRAM производить так же массово как DRAM, то память SRAM должна стоить всего в 3 раза дороже.
Сейчас спрос на SRAM намного превышает предложение. В видеокартах пишут уже на 100 MB SRAM собирается.

Цены уже не настолько различаются, кажется. Хотя у этих близкие характеристики:

1Mx16 DRAM 50ns NZ$10.31
1Mx16 SRAM 45ns NZ$16.54

https://nz.element14.com/integrated-silicon-solution-issi/is41lv16105d-50tli/dram-16mbit-tsop-ii-44/dp/2901162?ost=IS41LV16105D-50TLI

https://nz.element14.com/cypress-semiconductor/cy62167ev30ll-45bvxit/sram-16mbit-40-to-85deg-c/dp/2908570?ost=CY62167EV30LL-45BVXIT

tp_for_my_bunghole
()
Последнее исправление: tp_for_my_bunghole (всего исправлений: 1)
Ответ на: комментарий от DarkEld3r

Что должен иллюстрировать этот пример? Что в Go строки могут содержать мусор? Это правда преимущество?

Ken Thompson и Rob Pike разработали стандарт UTF-8. И язык Go.
Также Thompson создавал язык C.

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

В UTF-8 нелатинские символы состоят из двух байт и больше.

Но дело даже в этом. Строки это данные которые могут поступить в runtime от пользователя или по сети, и если программа на этом падает, то это плохо.
От кода со строками в принципе можно ожидать панику, и надо ловить.
Само сообщение о панике говорит что так начинается именно буква «з»:
thread 'main' panicked at 'byte index 1 is not a char boundary; it is inside 'з' (bytes 0..2) of `здравствуйте`'
Go по крайней мере не падает.

Для строк в Rust должна быть отдельная либа. Operator overloading для слайсов, разные функции. Наверное уже есть, и не одна.

tp_for_my_bunghole
()
Ответ на: комментарий от DarkEld3r

В коде Rust часто надо прибегать к использованию reference count типов Rc, RefCell.

Нет, не часто.

Если не часто, то наверное продолжу изучать.

tp_for_my_bunghole
()
Ответ на: комментарий от urxvt

За счет чего короче? Если представим, что это простой сервис: прочитать пару файлов, распарсить, запустить по таймеру что-то при каком-то условии

Очень тяжело измерять короткость на пальцах. Во-первых, не вся короткость продуктивна — например, сверхкороткие программы на MUMPS совершенно нечитаемы. Во-вторых, на обоих языках можно писать по-разному. Можно писать в стиле ООП на Go, и код будет огромным. Можно писать на Си с минимумом шаблонов и замыканиями, и код получится очень компактным. Однако, на Go можно писать в функциональном стиле, и получится тоже компактно.

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

byko3y ★★★★
()
Ответ на: комментарий от tp_for_my_bunghole

И во многих случаях всё решается передачей функций в параметры(в языках где привыкли к lambda)

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

Перегрузка операторов не нужна

Потому что разрабы Go так сказали? К слову, даже у них нет веcких аргументов для того, чтобы не реализовывать перегрузку операторов — вот просто не реализовали и всё:

https://golang.org/doc/faq#overloading

Полное отсутствие стандартного рантайма(для програмера) наверное и сделало Java недоступной для дебага

Это настолько бредятина, что я даже не знаю, с какой стороны начинать отвечать. Если ты не осилил жаву, то это не значит, что никто не осилил.

byko3y ★★★★
()
Ответ на: комментарий от tp_for_my_bunghole

А это какая патология когда на слове «параллелизм» начинают рассказывать про несинхронизированное выполнение кода в одном пространстве памяти?

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

byko3y ★★★★
()
Ответ на: комментарий от tp_for_my_bunghole

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

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

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

abcq ★★
()
Ответ на: комментарий от DarkEld3r

Неужели про «невозможность дебага» в джаве это правда?

Тут уже и про перегрузку операторов в жабе отожгли.

Просто не обращай внимания.

olelookoe ★★★
()
Ответ на: комментарий от kookoo

Можно на любом языке с ГЦ писать без ГЦ: просто не пиши на нём

починил, не благодари

shty ★★★★★
()
Ответ на: комментарий от DarkEld3r

«Everything should be made as simple as possible, but no simpler.» (c) А.Эйнштейн. В Rust явно перемудрили.

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

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

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

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

Внимание, важный тезис: индустрии нужно было просто ПОВЫСИТЬ НАДЕЖНОСТЬ работы программ. Не сделать их безупречно безопасными, потому что это практически невозможно, а просто повысить их надежность. Даеж если бы это был C++, но достаточно надежный C++ — индустрии было бы достаточно.

Напоминаю, что помимо ошибок работы с указателями в программах могут быть логические ошибки, которые также могут приводить к печальным последствиям. И эти ошибки Rust не просто не помогает искать — он их маскирует. Как говорит народная поговорка «за ёлками леса не видно»:

fn foo<T, I>(&self, first: T, callable: impl Fn(T) -> bool, iter: I) -> MyResult<T>
where
    T: FromStr,
    I: IntoIterator<Item = T>,

Это было объявление функции, если кто не понял. Не самое сложное объявление. В итоге Rust вполне недвусмысленно жертвует надежностью написания логики ради надежности работы с указателями. Не говоря уже про скорость разработки.

byko3y ★★★★
()
Ответ на: комментарий от DarkEld3r

Кстати, тут есть джависты? Неужели про «невозможность дебага» в джаве это правда? Что-то не верится

Джавистов тут нет, но про «невозможность» — это правда, он действительно не осилил отладку жавы.

byko3y ★★★★
()
Ответ на: комментарий от tp_for_my_bunghole

Cache-miss важен для научных последовательных расчётов. Для последовательной обработки данных.
Искусственный интеллект(логика принятия решений) не сильно коррелирует с кэшем

Мы обсуждали Rust и Go — там есть последовательные расчеты, и, как правило, нет ИИ, потому кэш решает, и потому давай не прыгать по сферам. Более того, локализация работы с данными является одним из ключевых аргументов за Go, без которого уже не совсем ясно, чем же Go лучше какого-нибудь современного языка для JVM, жонглирующего бесконечными указателями на коротенькие блоки памяти.

Если SRAM производить так же массово как DRAM, то память SRAM должна стоить всего в 3 раза дороже.
Сейчас спрос на SRAM намного превышает предложение. В видеокартах пишут уже на 100 MB SRAM собирается

Фабрики произведут ровно то, что им закажут. Если завтра возникнет спрос на SRAM — через две недели организуют массовое производство чипов. Но спроса нет — «индустрии» нужно много медленной памяти. Причем, даже у индустрии ИИ с ее нейронками — у той же Tesla K80, при 24 Гб SDRAM, регистровые файлы (самое больше SRAM хранилище) составляет 15 Мб (15x2x512k). Так что твой аргумент инвалид.

byko3y ★★★★
()
Ответ на: комментарий от tp_for_my_bunghole

Строки это данные которые могут поступить в runtime от пользователя или по сети, и если программа на этом падает, то это плохо

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

byko3y ★★★★
()
Ответ на: комментарий от tp_for_my_bunghole

Ken Thompson и Rob Pike разработали стандарт UTF-8. И язык Go.

Замечательно. И что? Это должно говорить о том, что в Go работа с юникодом реализована лучше, чем где-либо ещё? Или зачем этот факт тут нужен?

Аналогичный вопрос про С.

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

Безусловно. А ещё плохо когда «мусор на входе - мусор на выходе». Именно поэтому пришедшие из внешнего источника байты в строку можно превратить вызовом функции from_utf8, которая возвращает Result, а не падает. Также есть функция from_utf8_lossy, которая заменяет мусор специальным символом (�). И никакую панику ожидать и ловить не нужно.

Для строк в Rust должна быть отдельная либа.

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

DarkEld3r ★★★★★
()
Ответ на: комментарий от byko3y

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

Нет. Это твоя шиза. Разделяемая память без синхронизации.
Если для кого-то ещё актуальны школьные дискуссии о разделяемых данных в памяти без синхронизации, то ладно.
Если бы контр-аргументы были о том как сверхсложно синхронизировать, это ещё можно было бы принять.

tp_for_my_bunghole
()
Ответ на: комментарий от byko3y

Джавистов тут нет, но про «невозможность» — это правда, он действительно не осилил отладку жавы.

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

Можно принять аргумент как «а я не пользуюсь дебаггером», есть такие статьи. Это можно обосновать.

tp_for_my_bunghole
()
Ответ на: комментарий от tp_for_my_bunghole

Нет. Это твоя шиза. Разделяемая память без синхронизации

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

byko3y ★★★★
()
Ответ на: комментарий от byko3y

@byko3y

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

@DarkEld3r

Безусловно. А ещё плохо когда «мусор на входе - мусор на выходе»

Товарищи не поняли.

Приходит строка «Hello», slicing работает в Rust.
Приходит «здравствуйте» - panic, падает.

Нет никакого мусора в данных.

Или не знают что такое UTF-8, или не используют Rust.
То кто использует Rust привёл бы уже примеры либ для строк.

tp_for_my_bunghole
()
Последнее исправление: tp_for_my_bunghole (всего исправлений: 2)
Ответ на: комментарий от abcq

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

Расскажите это пользователям iPhone, у которых телефон бесконечно перезагружался от неправильного UTF-8 в сообщениях. Пусть лучше ерунду на экран выводит, но не падает.

X512 ★★★★★
()
Ответ на: комментарий от tp_for_my_bunghole

Приходит строка «Hello», slicing работает в Rust.
Приходит «здравствуйте» - panic, падает

Единственная адеватная претензия, которую я тут вижу — это что из коробки Rust не делает четкого разделения между безопасными операциями над строками и опасными. Для того, чтобы это разделение провести, нужно написать тонкую обертку над строками.

То кто использует Rust привёл бы уже примеры либ для строк

Для действительно продвинутой работы с юникодом с учетом семантики нужно что-то вроде

https://github.com/google/rust_icu

Иначе, по-хорошему, ты вообще не можешь брать индексы в UTF-8 строке, потому что ты непонятно куда попадешь — есть ведь символы, которе описываются несколькими рунами, потому даже проход по юникодным рунам не прокатывает. Зато можешь свободно конкатенировать строки или искать подстроку.

byko3y ★★★★
()
Ответ на: комментарий от byko3y

https://github.com/google/rust_icu

Это слишком. И это binding.
Но для Rust это можно считать допустимым подходом, наверно.

В Go range понимает utf8:

const nihongo = "日本語"
    for index, runeValue := range nihongo {
        fmt.Printf("%#U starts at byte position %d\n", runeValue, index)
}

Покажет 3 иероглифа и индекс начального байта для каждого.
Классический цикл как в C покажет только отдельные байты.
Больше возможностей в стандартном пакете unicode/utf8.

tp_for_my_bunghole
()
Последнее исправление: tp_for_my_bunghole (всего исправлений: 1)
Ответ на: комментарий от X512

Расскажите это пользователям iPhone, у которых телефон бесконечно перезагружался от неправильного UTF-8 в сообщениях. Пусть лучше ерунду на экран выводит, но не падает

Я ненавижу UTF-8. Это формат для передачи данных по сети, как base64 и gzip — но он решительно не подходит в роли внутреннего представления программы. Но америкосы, как обычно, будут тянуть совместимость до последнего, и спустя сто лет квантовые компьютеры с нейроинтерфейсом будут формировать дополненное восприятие человека с кодировкой UTF-8 и выводить неразборчивые описания в русских непатченных версиях прошивок. UTF-8 сделан из предположения, что большая часть всей информации будет находится в диапазоне ASCII — а это довольно сомнительное предположение.

И всё для чего? Для того, чтобы тащить из года в год кривые интерфейсы POSIX, которые уже давным давно не вывозят требований времени по асинхронному вводу-выводу, многопоточке, и, конечно же, обработке юникода. Посмотрите на винду — разрабы MS тупо переписали все системные интерфейсы под UTF-16, избавив от проблем 99% населения земли, при этом не раздули заметно потребление памяти (как с UCS-4). Большая часть дополнительных плоскостей юникода у меня вообще не печатается, и нужны они только для того, чтобы комитету было чем заняться.

И самое ироничное в этом угаре то, что в конце-концов даже для передачи по сети уже никто на самом деле в 2021 году не использует UTF-8, для самого сжатия используются более эффективные алгоритмы gzip/deflate/brotli, а если нужно больше скорости — есть lz4/lzo/std, которые жмут 500 мегабайт в секунду, и которые значительно более эффективны, чем UTF-8. А UTF-8 как бы используется для случая, если вы захотите посерфить интернет на IE 5 — в чём я вам желаю удачи.

byko3y ★★★★
()
Ответ на: комментарий от tp_for_my_bunghole

Это слишком. И это binding

Типы даннывх там родные растовские, но дергают функции из внешней либы.

В Go range понимает utf8

Он не разбивает их по символам, но разбивет их по рунам. Один символ может состоять из нескольких рун, например «y̆». Раст тоже умеет в str.chars(), только пользы от этого немного — для корректного распознавания символов нужно ICU или аналогичная по сложности библиотека.

byko3y ★★★★
()
Ответ на: комментарий от tp_for_my_bunghole

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

https://doc.rust-lang.org/std/primitive.str.html#method.chars https://doc.rust-lang.org/std/primitive.str.html#method.char_indices

mersinvald ★★★★★
()
Последнее исправление: mersinvald (всего исправлений: 1)
Ответ на: комментарий от X512

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

abcq ★★
()
Ответ на: комментарий от byko3y

Один символ может состоять из нескольких рун, например «y̆»

std lib.


import "unicode/utf8"

const nihongo = "日本語\u040E"  // short "y", "y breve"
for i, w := 0, 0; i < len(nihongo); i += w {
    runeValue, width := utf8.DecodeRuneInString(nihongo[i:])
    fmt.Printf("%#U starts at byte position %d\n", runeValue, i)
    w = width
}

@mersinvald

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

Получается нужны, для таких редких случаев. Врядли chars() руны знает.

tp_for_my_bunghole
()
Ответ на: комментарий от abcq

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

Можно. У меня так бойцы Access Violation хэндлили, пока прога не завалится замертво. Изначальный посыл ведь был о том, что если прога не сумела распознать символы, то должна игнорировать ошибку и выдавать какие-нибудь вопросики вместо корректных символов. Rust же при неаккуратном писании кода просто валится. Что делает еще более актуальным мой рассказа про абсурдность стремления разработчиков Rust сделать абсолютно безопасный язык.

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

byko3y ★★★★
()
Ответ на: комментарий от tp_for_my_bunghole

Один символ может состоять из нескольких рун, например «y̆»

std lib

И чо? Это та же самая работа с рунами — символы стандартная либа Go не умеет распознавать.

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

Получается нужны, для таких редких случаев. Врядли chars() руны знает

Это один в один тот же механизм, который в range и utf8.DecodeRune.

byko3y ★★★★
()
Ответ на: комментарий от byko3y

Я ненавижу UTF-8.

А я люблю. Всё остальное не нужно.

но он решительно не подходит в роли внутреннего представления программы.

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

разрабы MS тупо переписали все системные интерфейсы под UTF-16

Это такая же кодировка с переменным числом байт на символ как и UTF-8, смотрите суррогатные пары. Недавно в Windows 10 сделали поддержку UTF-8 в ANSI функциях, UTF-16 можно закапывать. Я считаю, что введение A/W функций и UTF-16 было ошибкой, как и многое другое в Windows NT. Windows 3.1 поддерживал multi-byte character encoding для японской кодировки Shift-JIS (некий японский аналог Юникода, помимо японского языка включает многие другие языки включая русский), могли бы и UTF-8 добавить.

X512 ★★★★★
()
Последнее исправление: X512 (всего исправлений: 2)
Ответ на: комментарий от byko3y

Можно. У меня так бойцы Access Violation хэндлили, пока прога не завалится замертво

Значит что-то таки упустили, 1:0 в пользу проги.

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

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

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

abcq ★★
()
Ответ на: комментарий от byko3y

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

В Обероне это уже давно сделано. Компилятор генерирует код так, чтобы ошибка обращения к памяти (NULL dereference, переполнение стека и т.д.) не приводила ни к чему плохому а также колбеки с внешним кодом выполняются в аналоге try {Callback();} catch (...) {} с их отключением в случае падения (чтобы не падало бесконечно, если надо можно включить колбек обратно) и показом сообщения об ошибке с трассировкой стека. Сами исключения и сигналы обычно не различаются, регистрируется только факт ошибки. В результате Оберон системы неубиваемые, процесс может спокойно пережить любые SIGSEGV, выходы за границы массива, переполнение стека, исключения и т.д..

X512 ★★★★★
()
Ответ на: комментарий от X512

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

Числа, даты, URL/email, взять слово из строки, валидировать последний введенный символ — короче говоря, почти любой разбор смысла строки. Даже на системном уровне приходится искать слэши в имени файла.

разрабы MS тупо переписали все системные интерфейсы под UTF-16

Это такая же кодировка с переменным числом байт на символ как и UTF-8, смотрите суррогатные пары

Дополнительными плоскостями пользуется 1% населения. Да, есть более популярные смайлики, которые все равно требуют специальной обработки, потому что требуют цветной печати, которая далеко не везде доступна: 🐌🐎.

Недавно в Windows 10 сделали поддержку UTF-8 в ANSI функциях, UTF-16 можно закапывать

Так ее сделали для совместимости с люниксом. Я не удивлюсь, если там до сих пор все системные функции при передаче UTF-8 строки делают MultiByteToWideChar, как это обычно делают A-версии функций. Зато, например, наконец заработает консоль питона под виндой.

byko3y ★★★★
()
Ответ на: комментарий от abcq

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

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

byko3y ★★★★
()
Ответ на: комментарий от byko3y

Числа, даты, URL/email, взять слово из строки, валидировать последний введенный символ

Ни с чем из этого не вижу проблем с UTF-8. Даже на голом Си всё спокойно делается.

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

Ищите строку "/", а не символ '/' и не будет никаких проблем. Главное правило — не использовать тип char для чего либо кроме хранения и копирования строк.

Я не удивлюсь, если там до сих пор все системные функции при передаче UTF-8 строки делают MultiByteToWideChar, как это обычно делают A-версии функций.

Так оно и есть. Внутри всё гвоздями прибито к UTF-16 начиная с первых Windows NT. В Windows 1.0 - ME было гибче сделано.

X512 ★★★★★
()
Последнее исправление: X512 (всего исправлений: 1)
Ответ на: комментарий от X512

В Обероне это уже давно сделано. Компилятор генерирует код так, чтобы ошибка обращения к памяти (NULL dereference, переполнение стека и т.д.) не приводила ни к чему плохому

Как он это делает? У него два отдельных стэка?

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

Сишная программа тоже может пережить SIGSEGV, только есть ли в этом смысл?

byko3y ★★★★
()
Ответ на: комментарий от byko3y

Как он это делает? У него два отдельных стэка?

В Windows используется защитная страница стека с однократным срабатыванием, которая восстанавливается после обработки исключения. В *NIX используется sigaltstack. Я недавно это всё под Haiku реализовывал.

Сишная программа тоже может пережить SIGSEGV, только есть ли в этом смысл?

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

X512 ★★★★★
()
Последнее исправление: X512 (всего исправлений: 1)
Ответ на: комментарий от X512

Ищите строку «/», а не символ '/' и не будет никаких проблем

Для того, чтобы найти строку «/», нужно последовательно разбить строку на руны.

Главное правило — не использовать тип char для чего либо кроме хранения и копирования строк

Мне кажется, что ты перепутал проблему формата хранения с проблемой API. Такое API издревле возникло из-за того, что параллельное сканирование строки намного быстрее, чем последовательное в UTF-8. Сейчас это так благодаря конвеерам и SIMD, а чуть раньше у x86 были специальные инструкции для сканирования байтов. Отсутствие возможности параллельного доступа — это ключевая проблема UTF-8. Разбить строку на блоки по 20 символов? Ололо, вы этого не хотели.

byko3y ★★★★
()
Ответ на: комментарий от byko3y

нужно последовательно разбить строку на руны.

Не нужно, достаточно pos = strstr(str, "/");. Или даже pos = strstr(str, "🐌");. В UTF-8 специально предусмотрено, что не будет ложных срабатываний при чтении мимо начала символов.

Разбить строку на блоки по 20 символов?

Зачем? Разбивайте на 20 char’ов и используйте SIMD, то что строки могут порезаться посередине символа вас волновать не должно.

X512 ★★★★★
()
Последнее исправление: X512 (всего исправлений: 2)
Ответ на: комментарий от X512

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

VirtualProtect(.., PAGE_GUARD, ..)? Оно работает только постранично, а потому защищает только от самых жестких ошибок.

В *NIX используется signaltstack

Ну оно защищает только обработчик сигнала. А как защититься от перезаписи адреса возврата в стэке?

Да, если она написана соответствующим образом так что никакая память не портится

Если программа и ее пользователь вообще смогут понять, портится ли эта память или нет — это самая больша проблема отладки программ на Си.

byko3y ★★★★
()
Ответ на: комментарий от tp_for_my_bunghole

chars_indices возвращает позицию и скалярное значение символа, из чего ширина руны тривиально вычисляется.

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=758cfffa1811cc862c893b880f8ee5db

mersinvald ★★★★★
()
Последнее исправление: mersinvald (всего исправлений: 1)
Ответ на: комментарий от byko3y

VirtualProtect(.., PAGE_GUARD, ..)?

Да.

Оно работает только постранично

Двух страниц в конце стека хватит чтобы перехватить все случаи переполнения стека. В случае выделения больших блоков памяти на стеке, делается проба страниц чтобы попасть в защитную страницу в случае переполнения стека. Используется следующий код:

  $StackAlloc				
code:33	PUSH	EAX	50
code:34	ADD	ECX, -5	83C1FB
code:37	JNS	code:41	7902
code:39	XOR	ECX, ECX	31C9
code:41	AND	ECX, -4	83E1FC
code:44	MOV	EAX, ECX	8BC1
code:46	ANDW	EAX, 4095	25FF0F0000
code:51	SUB	ESP, EAX	2BE0
code:53	MOV	EAX, ECX	8BC1
code:55	SHR	EAX, 12	C1E80C
code:58	JE	code:71	740B
code:60	PUSH	0	6A00
code:62	SUB	ESP, 4092	81ECFC0F0000
code:68	DEC	EAX	48
code:69	JNE	code:60	75F5
code:71	ADD	ECX, 8	83C108
code:74	MOV	EAX, [ESP+ECX-4]	8B440CFC
code:78	PUSH	EAX	50
code:79	MOV	EAX, [ESP+ECX-4]	8B440CFC
code:83	SHR	ECX, 2	C1E902
code:86	RET		C3

А как защититься от перезаписи адреса возврата в стэке?

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

X512 ★★★★★
()

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

tp_for_my_bunghole
()
Ответ на: комментарий от X512

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

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

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

Передать указатель на массив в стэке? Я плохо знаком с обероном, но хорошо занком с паскалем — в паскале подобные радости периодически захаживают. А переполнение — да, выдает segfault/a.v.

byko3y ★★★★
()
Ответ на: комментарий от X512

Не нужно, достаточно pos = strstr(str, «/»);. Или даже pos = strstr(str, «🐌»);. В UTF-8 специально предусмотрено, что не будет ложных срабатываний при чтении мимо начала символов

Убедил. А что делать, если поиск чуть сложнее? Например, с игнорированием регистра, или поиск по регуляркам? Самый убойный аргумент за UTF-16 — это простота использования. Програмист может исходить из допущения, что один печатный символ — это два байта, потому можно обращаться к символам по индексам — и будет практически всегда прав. Взять десять последних символов из UTF-16 — очень легко. Взять десять последних символов из UTF-8? Эм-м-м... это можно сделать.

Зачем? Разбивайте на 20 char’ов и используйте SIMD, то что строки могут порезаться посередине символа вас волновать не должно.

Бенчей не густо:
http://sqlite.1065341.n5.nabble.com/benchmarking-UTF8-vs-UTF16-encoded-databa...
https://github.com/brian-carroll/elm_c_wasm/blob/84cc59d960c05c521019f3f93142...

Если у тебя в основном ASCII текст, то UTF-8 в среднем выигрывает. Если у тебя какие-нибудь русские буквы, то ситуация неоднозначная. Если у тебя китайские буквы, то UTF-16 значительно выигрывает (в 1.5-2.5 раз на браузерах) — потому что китайский язык в UTF-8 занимает больше байт, чем в UTF-16, а обработка UTF-8 пусть и немного, но сложнее.

byko3y ★★★★
()
Ответ на: комментарий от byko3y

с игнорированием регистра

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

поиск по регуляркам

Специальная поддержка UTF-8 не требуется. Да и руками регулярные выражения обычно не делают, используют библиотеки.

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

А потом вставляете смайл (они сейчас активно используются) или редкий иероглиф и у вас начнут происходить странные вещи.

Взять десять последних символов из UTF-16 — очень легко.

Зачем? И если очень надо, это делается: UTF-8 можно итерировать в обратную сторону.

X512 ★★★★★
()
Ответ на: комментарий от byko3y

Так-то в любой программе ловить переполнение стэка не составляет труда.

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

Передать указатель на массив в стэке?

Нельзя. С указателями можно делать только две манипуляции: выделение памяти в куче со сборщиком мусора через NEW(ptr) или присвоение другому указателю. Указатели в Обероне — это по сути идентификаторы объектов в куче. Чтобы делать адресную арифметику, надо использовать unsafe режим (IMPORT SYSTEM).

Но можно передавать ссылки на что угодно в VAR-параметрах процедур. В случае массивов передаётся скрытый параметр длины массива если длина не указана в типе параметра.

MODULE A;
	
	PROCEDURE Fill (VAR a: ARRAY OF INTEGER);
		VAR i: INTEGER;
	BEGIN
		i := 0; WHILE i < LEN(a) DO
			a[i] := -i;
		INC(i) END;
	END Fill;
	
	PROCEDURE Do*;
		VAR a: ARRAY 10000H OF INTEGER; (* вызывает $StackAlloc потому что есть вероятность перехода за защитную страницу *)
	BEGIN
		Fill(a);
		HALT(0);
	END Do;
	
END A.

(!) A.Do
X512 ★★★★★
()
Последнее исправление: X512 (всего исправлений: 1)
Ответ на: комментарий от X512

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

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

поиск по регуляркам

Специальная поддержка UTF-8 не требуется

Да, потому что в либах для регулярок, как правило, для обобщений вроде «печатаемый символ» учитываются только ASCII символы — всё остальное идет лесом. Неудивительно, что на этом фоне UTF-8 «просто работает» — нет локалей, нет и проблем. Спасибо хоть число графем умеют считать.

А потом вставляете смайл (они сейчас активно используются) или редкий иероглиф и у вас начнут происходить странные вещи

А потом вставляете смайл (они сейчас активно используются) или редкий иероглиф и у вас начнут происходить странные вещи

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

Взять десять последних символов из UTF-16 — очень легко.

Зачем? И если очень надо, это делается: UTF-8 можно итерировать в обратную сторону

Вот я и пишу что можно. Но для UTF-16 это делать сильно проще. А ради чего усложнять это всё? Тупо для того, чтобы продать UTF-8 софт для ASCII-only заказчика с чуть более лучшими показателями производительности и потребления памяти, чем у UTF-16. А теперь прикинь, что у тебя в ОС-и системные идентификаторы записаны кириллицей. А для ASCII-only идентификаторов можно брать не просто UTF-8, а даже тупо ASCII.

byko3y ★★★★
()
Ответ на: комментарий от X512

С указателями можно делать только две манипуляции: выделение памяти в куче со сборщиком мусора через NEW(ptr) или присвоение другому указателю

Ой, это пройденный паскалем этап, который твой язык почему-то не прошел. Идея изначальна была в том, чтобы получить безопасность памяти еще в паскале, но потом очень быстро выяснилось, что в этой комнате с мягкими стенами ничего продвинутого сделать не получится, и очень быстро unsafe-ом становится всё подряд — и прощай безопасность.

byko3y ★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.