The OpenNET Project / Index page

[ новости /+++ | форум | теги | ]



Индекс форумов
Составление сообщения

Исходное сообщение
"Выпуск языка программирования Rust 1.55"
Отправлено Ordu, 12-Сен-21 22:48 
>>> WindowBuilder
> Да, для этого примера, передача владения работает как некая оптимизация,

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

>>> String::from_utf8(vec: Vec<u8>) -> Result<String, FromUTF8Error>
> Тут описан очень частный случай.

Постоянный. Я тебе привёл ещё один пример ровно того же самого -- BufReader. Такие "преобразования типов" типичны. В качестве ещё одного примера: drain итераторы, например, создаются именно так, и позволяют тебе деструктивно вынимать из контейнера один элемент за другим вплоть до исчерпания контейнера.

В std есть стандартные трейты Into и From, которые забирают один объект насовсем, и возвращают другой. И трейты эти используются на регулярной основе. Скажем, если я вызвал функцию, получил ошибку, и хочу завершить функцию с ошибкой, то в некоторых случаях я буду возвращать err.into() -- компилятор сам подберёт нужную реализацию трейта Into, которая преобразует ошибку полученного типа, в ошибку возвращаемого типа.

Хотя, если точнее, Into и From не обязательно забирают объект насовсем -- это зависит от того, реализует ли тип объекта трейт Copy. Как правило они не реализуют, потому что копировать налево и направо неявным образом -- зашквар.

> Тк. если создается уникод-строка, то чаще всего
> под неё понадобится больше памяти чем есть у "vec" и выделять
> новый блок памяти всё-равно придется.

Нет. Это в C/C++ придётся выделять из-за нуля-терминатора, который постоянно мешает работать со строками, там без своей реализации строк вообще невозможно жить. В rust'е же не придётся ничего выделять, потому что нет никаких терминаторов. String отличается от Vec только тем, что String гарантирует валидность utf8, обеспечивает некоторые операции, специфичные для utf8 строк, не обеспечивает некоторые операции Vec, которые не очень работают с utf8 строками.

> Если же String остается в UTF8 представлении, то эта функция - просто
> преобразование типов, которая скомпилируется в no-op.

Нет, это не noop. Result видишь? Происходит проверка валидности utf8, потому что String гарантирует, что если String-объект существует, то он содержит валидный utf8. Кроме того, String и Vec, хоть и являются оба умными указателями на один и тот же буфер, и оба сводятся к трём полям -- buf, size, len, -- но они _разные_ умные указатели, там может быть другое расположение полей в структуре, например. String::from_utf8, я полагаю, делает Vec::into_raw_parts(self) с возвращаемым значением по типу (usize, usize, *u8) /* обрати внимание, опять тот же паттерн, Vec уходит в into_raw_parts насовсем и безвозвратно */, а потом создаёт из этих raw-parts новый объект String. Если случится чудо, и новый объект String будет бинарно идентичен старому объекту Vec, то перетасовка полей превратится в noop. Если нет, то значит это не будет noop'ом.

>>> что будет если ты сделаешь:
>>> string s = "хеллоувролд";
>>> foo(std::move(s));
>>> std::cout << s << std::endl;
> Ничего "такого" не будет, cout "напечатает" пустую строку.

А, да точно. Я увидел на cppreference в примере упоминание UB, но при внимательном изучении там оказывается, что UB возникает не из-за неопределённого состояния s, а из-за того, что s.back() при том состоянии -- это UB. (Не, ты прикинь, ты вызываешь внешний API, и он тебе выкидывает не ошибку, не исключение, но UB. Ипануцца!) Но это значит, что foo(std::move(s)) -- это вызов конструктора std::string для создания пустой строки в s? Нахрена? Не, я понимаю, что в данном случае компилятор отоптимизирует и выкинет ненужный вызов (хотя не, не выкинет, s ведь используется в operator<<). Но в каких-то других случаях, этот конструктор может иметь side-эффекты, он может быть тормозным, он может не инлайниться, и мы не будем использовать s после вызова foo, но компилятор не сможет доказать, что выкидывание этого вызова -- это законная оптимизация, а не саботаж задумки программиста. Мы получаем абстракцию, которая нихрена не zero-cost.

>>> C++ программисты не любят внешние API
> Откуда такая уверенность?

Сравни использование внешних зависимостей в rust'е с таковым в C++. Как давно тебе приходилось тянуть в свой проект зависимостью специфическую реализацию хештаблички, которая долго создаётся, не умеет добавлять элементы после создания, зато выделяет памяти ровно столько сколько нужно (а не на 20-50% больше) и гарантирует отсутствие коллизий? Или пакет для работы со временем, который умеет всё, что нужно тебе, а не только то, что в прошлом веке сочли нужным те, кто писал стандарт для стандартной библиотеки? Или пакет для url-encode/url-decode? Пакет для парсинга markdown? Реализацию sha512? Base64? Пакет для форматирования размера в понятном человеку виде -- в смысле чтобы не "310438330", а "297M". Все эти вещи в C и C++ принято велосипедить. В C++ может чуть меньше, но всё равно велосипедить. Опеннет полагает это признаком квалификации программиста и способом избежать dependency hell'а, что может быть и так, но это также приводит к появлению тысяч полусырых велосипедов. Зато мейнтейнерам дистров жить проще.

>>> проще навелосипедить своё;
> Это зависит от конкретной ситуации, бывает что проще своё написать.

В расте крайне редко это проще. Если нет, того что надо, то скорее всего есть почти то, что надо, а это значит, что проще туда пулл-реквест закинуть, который доведёт "почти то что надо" до уровня "то что надо". И это проще, в частности, потому что даже внутренние, возможно недокументированные, API пакета довольно прозрачны, очень легко работать с чужим кодом. Ты видишь #[derive(Copy,Clone)] ты знаешь всё что тебе нужно знать о реакции объекта на присваивание его куда-то, тебе не надо выискивать код трёх-четырёх конструкторов и продираться через него, чтобы понять, что, собственно, имелось в виду. Ты смотришь в код, и понимаешь задумку автора этого кода.

>>> надо чтобы вызывающий код понимал как и что надо делать, чтобы не наступить на грабли.
> Да, надо. Читайте документацию (и документируйте свой АПИ).

Это не всегда работает. Попробуй продраться через документацию, скажем, к ffmpeg, понять когда безопасно вызывать free на буфер, который ты передавал в недра ffmpeg, когда не стоит этого делать, а когда ни-ни-ни-низзя этого делать совсем. В расте полезно проконсультироваться на этот счёт с документацией, но там такие вопросы _прозрачны_, и, кроме того, если ты где-то что-то поймёшь неверно (потому что документация кривая, или потому что ты читал невнимательно), то тебе компилятор потом об этом сообщит, выкинув ошибку компиляции. Лучшая документация на код -- это сам код. Документация может отстать от изменений кода, или не отстать, а измениться вместе с версией библиотеки, но твой код так и останется написанным по старой версии документации.

 

Ваше сообщение
Имя*:
EMail:
Для отправки ответов на email укажите знак ! перед адресом, например, !user@host.ru (!! - не показывать email).
Более тонкая настройка отправки ответов производится в профиле зарегистрированного участника форума.
Заголовок*:
Сообщение*:
 
При общении не допускается: неуважительное отношение к собеседнику, хамство, унизительное обращение, ненормативная лексика, переход на личности, агрессивное поведение, обесценивание собеседника, провоцирование флейма голословными и заведомо ложными заявлениями. Не отвечайте на сообщения, явно нарушающие правила - удаляются не только сами нарушения, но и все ответы на них. Лог модерирования.



Партнёры:
PostgresPro
Inferno Solutions
Hosting by Hoster.ru
Хостинг:

Закладки на сайте
Проследить за страницей
Created 1996-2024 by Maxim Chirkov
Добавить, Поддержать, Вебмастеру