LINUX.ORG.RU

Rust 1.10

 ,


0

4

Анонсирована очередная версия языка программирования Rust 1.10, разрабатываемого Mozilla совместно с сообществом.

Улучшения компилятора:

  • Добавлен новый тип крейта cdylib, предназначенный для экспорта C API. Основные отличия от dylib:
    • отсутствие метаданных;
    • разрешено LTO;
    • все библиотеки должны быть статически слинкованы;
    • экспортируются лишь те символы, которые помечены как extern. Например:
      pub fn foo() {} // не экспортируется
      #[no_mangle] pub extern fn bar() {} // экспортируется
    Для сравнения: «hello world» cdylib занимает 7.2КБ, а dylib - 2.4МБ.
  • Добавлена поддержка платформ i586-unknown-linux-gnu, i686-unknown-linux-musl, и armv7-linux-androideabi;
  • Снижено потребление памяти на ~100МБ при проверке типов;
  • Ускорена проверка T: Sized на 15%;
  • Улучшена кодогенерация при #[derive(Copy, Clone)].

Изменения в стандартной библиотеке:

Breaking changes!

  • AtomicBool теперь преобразуется в bool, а не isize. Демонстрация:
    use std::sync::atomic::AtomicBool;
    use std::mem::transmute;
    
    fn main() {
        let foo: bool = unsafe { transmute(AtomicBool::new(true)) };
    }
    
    На старых версиях компилятора будет ошибка;
  • time::Duration::new теперь будет паниковать при переполнении;
  • String::truncate теперь будет паниковать чуть меньше;
  • Небольшое изменение поведения макросов на этапе их парсинга: из :ty и :path следует :block;
  • Исправлен баг, связанный с гигиеной макросов. Следующий код будет валидным в устаревших версиях компилятора:
    fn main() {
        let x = true;
        macro_rules! foo { () => {
            let x = 0;
            macro_rules! bar { () => {x} }
            let _: bool = bar!();
            //^ `bar!()` использует первый `x` (который bool),
            //| а должен использовать второй `x` (который i32).
        }}
        foo! {};
    }
  • Переименование платформ:
    • arm-unknown-linux-gnueabi => arm-unknown-linux-gnu;
    • arm-unknown-linux-gnueabihf => arm-unknown-linux-gnu;
    • armv7-unknown-linux-gnueabihf => armv7-unknown-linux-gnu.
    Другими словами, изменены target_env, применяемые в conditional compilation.

Изменения в менеджере зависимостей Cargo:

  • Добавлен флаг --force, -f для подкоманды cargo install, предназначенной для загрузки исходных текстов из crates.io, их компиляции и установки в каталог ~/.cargo/bin. Это нововведение теперь позволит писать:
    cargo install FOO -f
    вместо:
    cargo uninstall FOO
    cargo install FOO
    Однако всё еще невозможно узнать, а требуется ли обновление вообще?
  • Диагностические сообщения теперь отправляются в stderr, а не в stdout;
  • С помощью флагов cargo doc --bin и cargo doc --lib можно выбрать: генерировать html документацию для проекта-приложения src/main.rs или проекта-библиотеки src/lib.rs;
  • В конфигурационном файле Cargo.toml, который можно встретить в корневом каталоге каждого проекта, теперь можно указать, каким образом макрос panic!() будет завершать приложение: unwind (по умолчанию) или abort;
  • Добавлен флаг cargo --explain FOO, поведение которого идентично rustc --explain FOO: показывает документацию по номеру ошибки;
  • В черный список имен крейтов добавлены ключевые слова раста, такие как fn, unsafe, let и прочее.

>>> Подробности



Проверено: tailgunner ()
Последнее исправление: cetjs2 (всего исправлений: 6)

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

писать this-> в C++

Мне кажется проблема в ->.

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

Потому что на таких объемах всякие неудачные эксперименты завершаются ничем

Если пол «экспериментом» подразумевается явный self, то этот «эксперимент» уже завершен успешно - на Python и Go написаны миллионы строк кода. Если речь о Rust с его моделью владения - здесь исход не ясен, да.

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

Если пол «экспериментом» подразумевается явный self, то этот «эксперимент» уже завершен успешно - на Python и Go написаны миллионы строк кода

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

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

Если речь о Rust с его моделью владения - здесь исход не ясен, да.

Что до Rust-а, то одна из его больших проблем — это слишком большое отличие синтаксиса от C-подобных языков. Слишком уж он не похож на то, к чему люди привыкли и что осваивается большинством разработчиков просто влет. Явные self-ы здесь так же не в пользу Rust-а.

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

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

Не будет. Мы имеем два удачных эксперимента.

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

слишком большое отличие синтаксиса от C-подобных языков
проблемы

Вы не поверите, но популярных языков, с отличным от C синтаксисом - навалом. Достаточно взять питон или менее популярных haskell, ну или еще менее популярный lisp. Они ни разу не похожи на C.

Имхо, синдром утёнка во всей красе.

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

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

Я не пойму: вы шутите или серьезно?

Мы же о явном self говорим? Явного self-а нет в большинство распространенных и востребованных статически-типизированных ООЯ, не только в C++. Даже в образце реализации ОО для статики, в Eiffel-е, ничего подобного нет.

В C++, конечно же, куча своих проблем, но вот проблемы с this-ом точно не те проблемы, из-за которых нужно менять С++ на Rust.

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

Вы не поверите, но популярных языков, с отличным от C синтаксисом - навалом. Достаточно взять питон или менее популярных haskell, ну или еще менее популярный lisp. Они ни разу не похожи на C.

Не поверю. Даже вы, начав их перечислять, сразу же начали говорит «менее популярный» и «еще менее популярный».

Если вам так нравится C++

Одна проблема в том, что альтернативы не лучше.

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

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

Попробуй просто s писать вместо self. На три буквы меньше.

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

сразу же начали говорит «менее популярный»

Относительно предыдущего языка.

Сказать, что питон менее популярный чем плюсы я не решусь.

Одна проблема в том, что альтернативы не лучше.

Кому как. Раст, для меня, решает множество проблем C++. Да, он не лучше C++ во всём, но часа весов всё равно склоняется к расту.

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

Вы определитесь, что с чем сравнивается: питон с плюсами или не-Си-шный синтаксис с Си-шным синтаксисом

Вы не поверите, но популярных языков, с отличным от C синтаксисом - навалом.

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

Ну так ту же агитируют за поголовный explicit. А ну как x — это локальная переменная? Это ж гарантированные баги у новичков :)

Боюсь вы не осознали смысла подхода explicit vs implicit. В расте x - это и есть локальная переменная. X - это никак не член структуры, к члену структуры обращаться self.x. Поэтому и эксплицитность, глядя на self.width = self.dpi * scaled_width сразу понятно, что откуда и куда.

anonymous
()
Ответ на: комментарий от quantum-troll

self.do_something(self.x, self.other_params);

Зачем метод запрашивает поле из self?

Затем, что он может быть вызван не с разными аргументами:

self.do_something(self.x, self.other_params);
self.do_something(self.y, self.other_other_params);
self.do_something(10, 100500);
anonymous
()
Ответ на: комментарий от eao197

Есть устоявшаяся практика, которой этот подход противопоставляется. Хотелось бы видеть какие-то аргументы «за», но пока он всего один: редкие ошибки с перекрытием области видимости.

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

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

int foo(const Bar&bar)const
- это не так элегантно как
fn foo(&mut self, bar: &mut Bar)->i32
. А в расте аннотаций много бывает, удобно, когда они расположены в одном месте. Ну и вообще, коль скоро они заменили перелопатили синтаксис объявления переменных, то причин косить под C++ в вопросе места размещения модификаторов self-а совсем нет.

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

Вторая причина: однообразие

Может еще имя модуля внутри него заставлять упоминать? А то как-то не однообразно получается. Не понятно сходу - это из этого модуля функция или нет. Да и use запретить по этому же поводу.

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

Т.е в том примере self.x используется как локальная переменная, вместо того, чтобы развести вычисления и изменение `self`?

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

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

struct Table {
    void update( void )
    {
        ...

        update_field( f1 );
        update_field( f2 );
    }

    void update_field( Field& f ) {
        ....
    }

    Field f1;
    Field f2;
};

что и как тут развести?

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

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

Хватит постить этот кусок говнокода. Да, в расте нет перегрузки. Говорят, это для того, чтоб вывод типа лучше работал. Но в расте нет и конструкторов, вместо них статические методы, возвращающие объект типа Self. Которые могут называться как угодно. Так что тот код можно было написать и так:

Some(Pattern::Color(ColorPattern::new(color.clone())))
и так:
Some(Pattern::from_color(&color))

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

Ваще-то да, мало. Собачья же работа.

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

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

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

fn compare<'a>(&'a self, other: &'a Self)-> Diff<'a>
Внутри тела функции также, меня вот в реализациях на C++-образных языках смущало такое:
auto diffA = m_A - other.m_A;
let diffA = self.a - other.a;
выглядит более целостно.

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

Хватит постить этот кусок говнокода

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

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

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

Ну как же, вот, например:

mod my {
    fn function() {
        println!("called `my::function()`");
    }

    pub fn indirect_call() {
        self::function();
    }
}

Тут можно писать self, а можно и не писать. Необнообразно. А если function не из my, то вообще неразбериха может быть и вызов совсем не той функции.

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

Ну в Rust с его не-ООП, да, таки соглашусь.

anonymous
()
Ответ на: комментарий от RazrFalcon
impl Color {
    fn new<T>(value: T) -> Color
        where Color: From<T>
    {
        Color::from(value)
    }
}

impl From<u8> for Color {
    fn from(value: u8) -> Color {
        Color { red: value }
    }
}

Кстати, а если хочется еще добавить возможность сделать вызов Color::new() без аргументов для создания дефолтного объекта, то как это можно сделать? Только через макрос или может есть аналоги void, variadic template из C++?

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

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

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

В случае Python на таких объемах люди утыкаются в «динамику», а не в self. Self, как раз, на таких объемах, пожалуй, одна из немногих вещей, которые помогают.

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

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

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

В случае Python на таких объемах люди утыкаются в «динамику», а не в self.

Я говорил про статику. В частности, наблюдал такое в C++ и Java, когда некоторые разработчики писали обращения к членам через this (т.е. this->x или this.x).

eao197 ★★★★★
()

Любая рекуррентная структура глубиной порядка размера стека всё так же вызывает креш рантайма при деаллокации: http://ideone.com/mfzeNy

tailgunner, как евангелист раста, объясни почему идиоматичный код без всяких unsafe вещей приводит к непредсказуемым крешам в зависимости от размера задачи?

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

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

Ясно, ну на самом деле тут и макрос очень легко пишется. Хоть и не все случаи им можно охватить, но такой простой вариант - без проблем. Вот только после того, как я в порядке эксперимента попробовал обмазаться трейтами и макросами, у меня немного двойственное чувство. С одной стороны реализуется это все просто, с другой - смотришь на макрос вместо функции, на реализации трейтов, и понимаешь авторов Servo, которые просто пишут все в одну строку. Это не претензия к Rust, а скорее попытка осмысления - как на нем должны реализоваться подобные случаи. Хотя может, если подвезут дефолтные аргументы (почему их вообще не добавили сразу?!), некоторые вещи станут проще.

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

почему идиоматичный код без всяких unsafe вещей приводит к непредсказуемым крешам
непредсказуемым крешам

stderr: thread '<main>' has overflowed its stack

Во-первых, rust гарантирует защиту от некоторых ошибок при работе с памятью, а не от всех.

Во-вторых, предложите алгоритм проверки stack overflow в статике. Желательно со ссылкой на RFC.

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

Любая рекуррентная структура глубиной порядка размера стека всё так же вызывает креш рантайма при деаллокации: http://ideone.com/mfzeNy

У меня на stable и nightly всё нормально.

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

Во-вторых, предложите алгоритм проверки stack overflow в статике

Это проблемы кривого рантайма и дизайна языка, а не проверок в статике. Структура данных выделяется в куче, а не на стеке, причём выделяется успешно (hello world успевает распечататься). Падение происходит в самом рантайме при автоматическом освобождении ресурсов, т.е. в коде, который мы не писали.

В любом языке с GC такой ошибки нет, кстати.

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

Попробуй поиграться с магической константой (увеличить). Как я понимаю, она действительно магическая, т.к. зависит, к примеру, от кол-ва локальных переменных внутри функций рантайма. У меня нет rust на десктопе, не могу протестить на других версиях.

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

В любом языке с GC такой ошибки нет, кстати.

Там своих проблем хватает. Но адептам не понять.

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

Хз. Баг ideone. В 1.9.0 всё нормально работает.

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

Любая рекуррентная структура глубиной порядка размера стека всё так же вызывает креш рантайма при деаллокации: http://ideone.com/mfzeNy

На playground это программа выводит «Hello, world!» и завершается. Если ты хочешь спросить «почему я могу исчерпать стек», то ответ - «потому что стек - конечный ресурс»; если ты хочешь спросить «почему я не могу перехватить это событие и обработать его» - потому что рантайм не доделан.

почему идиоматичный код без всяких unsafe вещей приводит к непредсказуемым крешам в зависимости от размера задачи?

Почему ты называешь креш, который можешь спровоцировать, «непредсказуемым»?

как евангелист раста

Я еще меньший евангелист Rust, чем ты - его хейтер.

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

explicit vs implicit

В случае аргументов - это явная глупость, которая приводит к костылям. А попытка сделать костыли короче - к уродству вроде:

OpenOptions::new().read(true).open("foo.txt");

Лучше уж бы сделали так:

File::open_with_options("foo.txt", OpenOptions::new().read(true));
anonymous
()
Ответ на: комментарий от RazrFalcon

С каких пор «чеёнинг» - уродство?

С тех пор, как им пытаются заткнуть пробелы в языке. Если тебе надо открыть файл - ты пишешь File::open, но если тебе нужно добавить опцию - ты выкидываешь эту конструкцию нахрен и переписываешь код задом на перед.

https://github.com/rust-lang/rfcs

Не вижу смысла и перспективы.

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

Какие проблемы?

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

В С ты просто проходишь while-циклом по списку, удаляя элементы за const ram — stack overflow нет.

В haskell GC сканирует call stack'и процесса и за const ram копирует в другое поколение достижимые данные из текущего поколения, при этом мусор не трогается вообще — stack overflow нет. А можно и простым malloc'ом воспользоваться и делать всё как в C.

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

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

Почему ты называешь креш, который можешь спровоцировать, «непредсказуемым»?

А ты можешь рассчитать безопасную длину списка?

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

Кстати, а если хочется еще добавить возможность сделать вызов Color::new() без аргументов для создания дефолтного объекта, то как это можно сделать? Только через макрос или может есть аналоги void, variadic template из C++?

Для произвольного количества аргументов следует применить pattern builder. Для дефолтного значения логичнее использовать трейт Default, тогда можно будет писать так: `Color::default()`. Дженерик лучше задавать при объявлении типа: `struct Color<T>{ .. }`. Трейт Into автоматически реализуется для From, поэтому конструктор можно записать иначе:

impl<T> Color<T> {
    fn new<I: Into<Self>>(value: I) -> Self {
        value.into()
    }
}
Однако для цвета вместо `From` лучше сделать методы `from_xrgb`, `from_rgbx`, `from_argb`, `from_rgba` и т.п.

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

Для произвольного количества аргументов следует применить pattern builder

Для такого простого случая это будет слишком монструозно.

Для дефолтного значения логичнее использовать трейт Default

А вот это действительно логично. Спасибо.

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

Почему ты называешь креш, который можешь спровоцировать, «непредсказуемым»?

А ты можешь рассчитать безопасную длину списка?

Если бы мне это было надо - да, я бы рассчитал. Просто опытным путем. Какие проблемы.

И только в языках с ARC

Мде. У тебя в тестовой программе нет ARC. И, почему-то мне кажется, что способ обойти конкретно эту проблему придуман давно и практикующим программистам на Rust известен.

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

Если бы мне это было надо - да, я бы рассчитал. Просто опытным путем. Какие проблемы.

А разве размер стека - всегда постоянная величина?

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

А разве доступный размер стека - всегда постоянная величина?

Добавлено/поправлено.

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