Обновить
4

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

1
Подписчики
Отправить сообщение

Только вот использовать их в такой комбинации невозможно в принципе. Если точнее:


  • два явных импорта с одним именем конфликтуют;
  • если конфликтует явный импорт и use module::*;, всегда используется явный;
  • если явного нет, а глоб-импортов, содержащих это имя, больше одного, всегда выдаётся ошибка.

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


Если вы и правда считаете что это лишняя ифнормация и можно сделать лучше, откройте pre-RFC обсуждение на IRLO, я думаю там вам смогут привести куда более веские аргументы.

А при чём здесь, собственно, internals и pre-RFC? Мы же обсуждаем не изменение языка, а то, как имеющимися фичами языка наиболее точно выразить трейт Map крейта dashmap, нет? Если уж здесь что-нибудь и имело бы смысл открывать, так это PR в dashmap, рефакторящий его на другой вариант… но я не уверен, стоит ли лезть со внутренним рефакторингом без намерения становиться долгосрочным контрибьютором.

Просто раст не запрещает объявление типов с одинаковым именем.

Вообще-то запрещает, можете проверить сами :-)


Кроме того, теперь запись (...) и (...) это совершенно разные вещи...

Нет, не разные, по-прежнему Key (K) в скоупе и Self::Key — это независимые типы (хотя да, строчка type Key = Key; будет выглядеть забавно).


… и переименование генериков в библиотеке становится ломающим изменением (по этим же причинам).

Во-первых, это не генерики, а ассоциированные типы. Во-вторых, да, в отличие от генериков они именованные и это не бага, это фича. Использование вместо них генериков — это как out-параметры: порой так делают, но обычно это считается не особо идеоматичным.


И разница как раз в том, что


… зато теперь нельзя сразу понять, сколько параметров.

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


Для сравнения давайте посмотрим в стандартную библиотеку на пару похожих трейтов: AsRef и Deref. У обоих есть единственный метод с одинаковой сигнатурой fn(&Self) -> &Something. Тем не менее, они принципиально отличаются.
AsRef имеет параметр типа, что позволяет ему иметь много реализаций. Один и тот же строковый тип String удовлетворяет сразу многим конкретным реализациям: AsRef<[u8]>, AsRef<str>, AsRef<OsStr>, AsRef<Path> — и это только в стандартной библиотеке.
Deref же параметров не имеет, а имеет взамен ассоциированный тип. Для каждого типа, реализующего Deref, жёстко задано, в какой именно тип он переводит (и в случае String это именно str). У пользователя нету выбора, какую именно реализацию использовать.
Вот этот служебный трейт Map, на мой взгляд, ближе ко второму варианту. Логика его использования не предусматривает вариант, в котором один и тот же тип имеет несколько реализаций, различающихся параметрами, из которых пользователь выбирает нужную — а именно в этом и есть разница.

Я не вполне понял постановку вопроса. Это три разных варианта того, как можно было бы сделать один и тот же трейт?

Как есть
pub trait Map<'a, K: 'a + Eq + Hash, V: 'a, S: 'a + Clone + BuildHasher> {
    fn _shard_count(&self) -> usize;
    ...
}

impl<'a, K: 'a + Eq + Hash, V: 'a, S: 'a + BuildHasher + Clone> Map<'a, K, V, S>
    for DashMap<K, V, S>
{
    #[inline]
    fn _shard_count(&self) -> usize {
        self.shards.len()
    }
    ...
}

Как должно бы быть лучше
pub trait Map<'a> {
    type Key: 'a + Eq + Hash;
    type Value: 'a;
    type RandomState: 'a + Clone + BuildHasher;

    fn _shard_count(&self) -> usize;
    [...]
}

impl<'a, K: 'a + Eq + Hash, V: 'a, S: 'a + BuildHasher + Clone> Map<'a>
    for DashMap<K, V, S>
{
    type Key = K;
    type Value = V;
    type RandomState = S;

    #[inline]
    fn _shard_count(&self) -> usize {
        self.shards.len()
    }
    [...]
}

Вот потому я и говорю, что у трейта изначально неоптимальное определение, на мой взгляд.

Примерно вот так


impl<'a, 'i, M: Map<'a>> Iterator for Iter<'a, 'i, M>
 {
    type Item = RefMulti<'a, M::Key, M::Value, M::State>;

если там дальше пробем не возникнет.

Не "магически вывелись", а прописаны как ассоциированные типы в трейте Map и его реализации. Примерно вот так:


type Item = RefMutMulti<'a, M::Key, M::Value, M::S>;

если вообще не сделать параметром тип M, а не эти три по отдельности.

Вообще, я бы сказал на первый взгляд, что лишнее здесь всё, кроме 'a и M — просто потому, что трейт Map определён неправильно. K, V, S должны бы быть ассоциированными типами, а не параметрами.
Параметры имеют смысл в основном тогда, когда реализаций с несколькими параметрами может быть несколько… но я очень сильно сомневаюсь в структуре, для которой имели бы смысл реализации Map с разными K, V, S.

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

По-моему, все планы с полётом на Марс и возвращением кораблей (а тем более людей) подразумевают разворачивание там завода по производству топлива. Обсуждаемая программа этого не подразумевает.


Ключевых момента в этой таблице два. Во-первых, автор утверждает, что полностью заправленный Starship на орбите имеет 9.5 км/с запаса скорости (delta V), что, по его оценкам, недостаточно для перелёта к Луне, посадки, взлёта и возвращения на Землю. Отсюда необходимость дозаправки на окололунной орбите, следовательно, возникает танкер, который тоже нужно заправлять на околоземной орбите.
Во-вторых, "после дозаправки". Автор оценивает количество топлива, выводимое одним Starship, в 100 тонн (заявленная полезная нагрузка на низкую околоземную орбиту) и приходит к выводу, что каждая дозаправка на орбите — это десяток запусков танкеров, а не дозаправка от одного танкера, как в красивых демо-видео. Соответственно, в случае Марса "после дозаправки" — это тот же десяток запусков.

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

Самый простой способ проверки, что именно делает редактор:


$ ls -i test.sh
33702972 test.sh
$ vim test.sh # edit the file
$ ls -i test.sh
33702972 test.sh
$ sed -e 's/10/20/' -i test.sh
$ ls -i test.sh
33717589 test.sh

если inode изменяется, то редактор заменил старый файл новым; если остаётся прежним — отредактировал файл на месте.

А я о чём:


sed - запись временного файла и замена

./ OPEN test.sh
./ CREATE sedGpzLhh
./ OPEN sedGpzLhh
./ ACCESS test.sh
./ ATTRIB sedGpzLhh
./ ATTRIB sedGpzLhh
./ CLOSE_NOWRITE,CLOSE test.sh
./ MODIFY sedGpzLhh
./ CLOSE_WRITE,CLOSE sedGpzLhh
./ MOVED_FROM sedGpzLhh
./ MOVED_TO test.sh


vim - изменение содержимого файла

./ OPEN test.sh
./ CREATE .test.sh.swp
./ OPEN .test.sh.swp
./ CREATE .test.sh.swx
./ OPEN .test.sh.swx
./ CLOSE_WRITE,CLOSE .test.sh.swx
./ DELETE .test.sh.swx
./ CLOSE_WRITE,CLOSE .test.sh.swp
./ DELETE .test.sh.swp
./ CREATE .test.sh.swp
./ OPEN .test.sh.swp
./ MODIFY .test.sh.swp
./ MODIFY .test.sh.swp
./ ATTRIB .test.sh.swp
./ CLOSE_NOWRITE,CLOSE test.sh
./ OPEN test.sh
./ ACCESS test.sh
./ CLOSE_NOWRITE,CLOSE test.sh
./ OPEN,ISDIR
./ CLOSE_NOWRITE,CLOSE,ISDIR
./ OPEN,ISDIR
./ CLOSE_NOWRITE,CLOSE,ISDIR
./ OPEN,ISDIR
./ CLOSE_NOWRITE,CLOSE,ISDIR
./ MODIFY .test.sh.swp
./ MODIFY .test.sh.swp
./ OPEN test.sh
./ MODIFY test.sh
./ ATTRIB test.sh
./ CLOSE_WRITE,CLOSE test.sh
./ ATTRIB test.sh
./ MODIFY .test.sh.swp
./ CLOSE_WRITE,CLOSE .test.sh.swp
./ DELETE .test.sh.swp


Извините, gedit не проверю — у меня Plasma, он не установлен.

(промахнулся местом ответа)

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

Замену в чём именно? В идеале предполагается заменить им LLVM для дебаг-билдов, потому что производительность LLVM — один из ключевых факторов, ограничивающих скорость их сборки.


Можно ли написать на Расте бэкенд с производительностью кода не хуже, чем у LLVM? Зависит от того, сколько человеко-лет в него вложить...

Я не так много вращался в Джаве и довольно много (но колхозно) вращаюсь в Шарпе, но мне, черт побери, нравится многословность и CamelCase. Это сделано для людей, а все остальное решает IntelliSense и нормально спроектированные библиотеки.

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


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


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

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


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

Написать можно, но пока никто не сделал.

Cranelift-бэкенд потихоньку прогрессирует, кстати.


можно ли на раст писать под микроконтроллеры там где нет ОС? Голый раст и компиляция под конкретное железо. AtMega, PIC, STM?

Да, этим разные безбашенные люди занимаются. Почему безбашенные? Потому что там нужны фичи, которых в стабильном Rust нету, есть только Nightly.

Если я ничего не путаю, базовый embedded на стабильной версии работает.

Так в том-то и дело, что меня вполне устроит в качестве ответа линтер, который банит потенциально корректный код. Если он скажет "написанный так код потенциально опасен, так нельзя, пользуйтесь абстракцией такой-то" — оно и к лучшему. Если мне действительно будет нужно — найду в документации, как разрешить данное конкретное нарушение правил линтера (а заодно прочитаю, чем именно оно опасно, на что именно нужно проверить код вручную и о чём нужно предупредить комментарием следующего разработчика).


Не устраивает меня ситуация, в которой "разумеется, C++ позволяет всё это безопасно написать, но чтобы быть уверенным хотя бы на 90% в безопасности написанного, обязательно необходим ревьюер с опытом в современном C++ от N лет". Новые проекты в опенсорсе нередко начинаются с одного человека (или маленькой команды), и я не понимаю, как сейчас начать проект на современном C++ без хождения по всем стандартным граблям с нуля.

Информация

В рейтинге
5 463-й
Зарегистрирован
Активность