Команда Rust рада представить выпуск Rust 1.17.0. Rust — это системный язык программирования, нацеленный на безопасность, скорость и параллельное выполнение кода.


Если у вас установлена предыдущая версия Rust, то для обновления достаточно выполнить:


$ rustup update stable

Если у вас ещё не установлен Rust, то вы можете установить rustup c соответствующей страницы нашего веб-сайта и ознакомиться с подробным примечанием к выпуску 1.17.0 на GitHub.


Что вошло в стабильную версию 1.17.0


Выпуск Rust 1.17.0 в основном вносит небольшие улучшения, преимущественно касающиеся удобства использования. Например, время жизни 'static теперь автоматически подразумевается для констант или статических переменных. При создании константы или статической переменной:



const NAME: &'static str = "Ferris";
static NAME: &'static str = "Ferris";

Rust 1.17 позволит вам больше не писать 'static, так как это единственное время жизни, которое имеет смысл:


const NAME: &str = "Ferris";
static NAME: &str = "Ferris";

В некоторых ситуациях это позволит избавиться от лишних повторений:


// было
const NAMES: &'static [&'static str; 2] = &["Ferris", "Bors"];

// стало
const NAMES: &[&str; 2] = &["Ferris", "Bors"];

Другим подобным косметическим улучшением является "короткая инициализация полей". Подобно ECMAScript 6, который называет это "Сокращение Значения Свойства Объектного Литерала" ("Object Literal Property Value Shorthand"), дублирование может быть удалено при объявлении структур, к примеру:


// определение
struct Point {
    x: i32,
    y: i32,
}

let x = 5;
let y = 6;

// было
let p = Point {
    x: x,
    y: y,
};

// стало
let p = Point {
    x,
    y,
};

То есть форма записи x, y будет предполагать, что значения x и y соответствуют переменным с аналогичными именами, находящимися в данной области видимости.


Другое маленькое, но полезное улучшение касается в основном новичков в Rust, которые пытаются использовать +, чтобы соединить две &str вместе. Но это не работает, соединить вы можете лишь String + &str. Поэтому, было добавлено новое сообщение об ошибке, чтобы помочь пользователям, которые совершают подобную ошибку:


// код
"foo" + "bar"

// было
error[E0369]: binary operation `+` cannot be applied to type `&'static str`
 --> <anon>:2:5
  |
2 |     "foo" + "bar"
  |     ^^^^^
  |
note: an implementation of `std::ops::Add` might be missing for `&'static str`
 --> <anon>:2:5
  |
2 |     "foo" + "bar"
  |     ^^^^^

// стало
error[E0369]: binary operation `+` cannot be applied to type `&'static str`
 --> <anon>:2:5
  |
2 |     "foo" + "bar"
  |     ^^^^^
  |
  = note: `+` can't be used to concatenate two `&str` strings
help: to_owned() can be used to create an owned `String` from a string
reference. String concatenation appends the string on the right to the string on
the left and may require reallocation. This requires ownership of the string on
the left.
  |     "foo".to_owned() + "bar"

При использовании сценариев сборки Cargo вы должны указать расположение скрипта в вашем Cargo.toml. Однако, подавляющее большинство людей писали build = "build.rs", тем самым используя файл build.rs, расположенный в корне проекта. Теперь это соглашение поддерживается самим Cargo, и будет использовано по умолчанию, если существует файл build.rs. Мы предупреждали об этом изменении в течение нескольких последних выпусков. Вы также можете использовать build = false для отказа от этого соглашения.


В этом выпуске удалена старая система сборки на основе Makefile. Новая система, анонсированная в Rust 1.15, написана на Rust и в основном использует Cargo для управления сборкой. На данный момент она уже достаточно зрелая, чтобы быть единственной системой сборки.


В рамках этого изменения, пакеты из crates.io теперь можно использовать в системе сборки Rust. Первым был добавлен mdBook, и он теперь используется при сборки нашей разнообразной книжной документации:



Обратите внимание на ссылки на соответствующие репозитории; документы были перемещены из основного дерева. Кроме того, мы добавили четвертую книгу, которая все еще расположена в основном дереве: Книга "Нестабильные возможности Rust". Она описывает нестабильные возможности, содержит ссылки на задачи, связанные с их стабилизацией, и может содержать исходную документацию. Если есть возможность, которую вы хотите увидеть стабилизированной, то пожалуйста, примите участие в ее обсуждении!


Несколько выпусков назад rustup перестал по умолчанию устанавливать документацию. Мы внесли это изменение, чтобы немного разгрузить канал, а также потому, что не все пользователи в действительности хотят хранить локальную копию документации. Однако это создало ловушку: некоторые пользователи не были в курсе, что произошло изменение, и заметили бы это, только если бы у них пропало подключение к Интернету. Кроме того, некоторые пользователи хотели иметь локальную копию документации, независимо от их подключения. Таким образом, мы откатили данное изменение, и документация снова устанавливается по умолчанию.


Наконец, хотя этот выпуск и полон улучшений, но есть один маленький шажок назад, о котором мы хотим сообщить вам с сожалением. В Windows была выпущена Visual Studio 2017, и Microsoft изменила структуру установки программного обеспечения. Rust не может автоматически определить её местоположение, и хотя мы работали над необходимыми изменениями, но не успели к этому выпуску. До тех пор, Visual Studio 2015 все еще работает нормально, или же вы можете запустить vcvars.bat в командной строке. Мы надеемся, что эта работа будет выполнена в кратчайшие сроки.


Подробнее смотрите примечания к выпуску.


Стабилизация библиотек


Был стабилизирован 21 новый интерфейс:


  • Arc::into_raw и Rc::into_raw позволят вам забрать Arc или Rc и получить сырой указатель.
  • Arc::from_raw и Rc::from_raw позволят вам забрать сырой указатель и получить Arc или Rc.
  • Arc::ptr_eq и Rc::ptr_eq возвращает true если оба Arc или оба Rc указывают на одно и то же значение (не обязательно значения, которые сравниваются, равны).
  • Ordering::then позволит вам сцепить два Ordering вместе, и Ordering::then_with позволит сделать это с помощью функции.
  • BTreeMap::range позволит вам итерировать лишь по части BTreeMap, и BTreeMap::range_mut позволит вам сделать это с возможностью изменения. collections::Bound может дать вам еще больше контроля.
  • process::abort будет полностью завершать процесс анормальным образом.
  • ptr::read_unaligned и ptr::write_unaligned аналогичны ptr::read и ptr::write, но без требований к выравниванию.
  • Result::expect_err зеркально подобен Result::expect, то есть работает с вариантом Err, а не с вариантом Ok.
  • Cell::swap аналогичен std::mem::swap, но позволяет вам делать это с &Cell вместо &mut T.
  • Cell::replace аналогичен std::mem::replace, но позволяет вам делать это с &Cell вместо &mut T.
  • Cell::into_inner позволит вам взять Cell, и извлечь его значение.
  • Cell::take позволит вам забрать значение Cell наружу, заменив его на Default::default.

Что касается других изменений, для многих методов Cell<T> требовалось ограничение T: Copy, но теперь это требование значительно ослаблено.


Box<T> теперь реализует более дюжины новых преобразований с помощью From.


SocketAddr и IpAddr также теперь имеют несколько новых преобразований. Раньше вы должны были писать код вроде этого:


"127.0.0.1:3000".parse().unwrap()

Сейчас же вы можете писать


SocketAddr::from(([127, 0, 0, 1], 3000))
// или
([127, 0, 0, 1], 3000).into())

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


Обратная трассировка теперь имеет более приятное форматирование, опуская некоторые незначительные детали по умолчанию. Например, полная обратная трассировка:


thread 'main' panicked at 'explicit panic', foo.rs:2
stack backtrace:
   1:     0x55c39a23372c - std::sys::imp::backtrace::tracing::imp::write::hf33ae72d0baa11ed
                        at /buildslave/rust-buildbot/slave/stable-dist-rustc-linux/build/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs:42
   2:     0x55c39a23571e - std::panicking::default_hook::{{closure}}::h59672b733cc6a455
                        at /buildslave/rust-buildbot/slave/stable-dist-rustc-linux/build/src/libstd/panicking.rs:351
   3:     0x55c39a235324 - std::panicking::default_hook::h1670459d2f3f8843
                        at /buildslave/rust-buildbot/slave/stable-dist-rustc-linux/build/src/libstd/panicking.rs:367
   4:     0x55c39a235afb - std::panicking::rust_panic_with_hook::hcf0ddb069e7beee7
                        at /buildslave/rust-buildbot/slave/stable-dist-rustc-linux/build/src/libstd/panicking.rs:555
   5:     0x55c39a22e866 - std::panicking::begin_panic::heb433e9aa28a7408
   6:     0x55c39a22e9bf - foo::main::hd216d4a160fcce19
   7:     0x55c39a23d44a - __rust_maybe_catch_panic
                        at /buildslave/rust-buildbot/slave/stable-dist-rustc-linux/build/src/libpanic_unwind/lib.rs:98
   8:     0x55c39a236006 - std::rt::lang_start::hd7c880a37a646e81
                        at /buildslave/rust-buildbot/slave/stable-dist-rustc-linux/build/src/libstd/panicking.rs:436
                        at /buildslave/rust-buildbot/slave/stable-dist-rustc-linux/build/src/libstd/panic.rs:361
                        at /buildslave/rust-buildbot/slave/stable-dist-rustc-linux/build/src/libstd/rt.rs:57
   9:     0x55c39a22e9e9 - main
  10:     0x7f5e5ed3382f - __libc_start_main
  11:     0x55c39a22e6b8 - _start
  12:                0x0 - <unknown>

теперь имеет вид


thread 'main' panicked at 'explicit panic', foo.rs:2
stack backtrace:
   0: std::sys::imp::backtrace::tracing::imp::unwind_backtrace
             at /checkout/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs:49
   1: std::sys_common::backtrace::_print
             at /checkout/src/libstd/sys_common/backtrace.rs:71
   2: std::panicking::default_hook::{{closure}}
             at /checkout/src/libstd/sys_common/backtrace.rs:60
             at /checkout/src/libstd/panicking.rs:355
   3: std::panicking::default_hook
             at /checkout/src/libstd/panicking.rs:371
   4: std::panicking::rust_panic_with_hook
             at /checkout/src/libstd/panicking.rs:549
   5: std::panicking::begin_panic
   6: foo::main
   7: __rust_maybe_catch_panic
             at /checkout/src/libpanic_unwind/lib.rs:98
   8: std::rt::lang_start
             at /checkout/src/libstd/panicking.rs:433
             at /checkout/src/libstd/panic.rs:361
             at /checkout/src/libstd/rt.rs:57
   9: main
  10: __libc_start_main
  11: _start

по умолчанию. Вы можете установить переменную окружения RUST_BACKTRACE = full, чтобы получить полную обратную трассировку. В будущем мы планируем убрать еще больше лишней информации; См. эту ошибку.


Подробнее смотрите примечания к выпуску.


Возможности Cargo


Помимо ранее упомянутых изменений, касающихся build.rs, у Cargo есть еще несколько новых улучшений. cargo check --all и cargo run --package — два отсутствовавших до этого момента флага, которые теперь поддерживаются.


Теперь вы можете игнорировать проверки отзыва SSL. Конечно, по умолчанию проверка все еще производится.


Новое поле в Cargo.toml, required-features, позволяет указать конкретные возможности, которые должны быть установлены для цели, которую нужно собрать. Вот пример: предположим, что мы пишем контейнер, который взаимодействует с базами данных, и хотим, чтобы он поддерживал несколько баз данных. Мы могли бы это сделать в нашем Cargo.toml:


[features]
# ...
postgres = []
sqlite = []
tools = []

Возможность tools позволяет нам включить дополнительные инструменты, а возможности postgres и sqlite указывают, какие базы данных мы хотим поддерживать.


Раньше cargo build пыталась собрать все цели, которые вам нужны. Но что, если у нас есть файл src/bin/postgres-tool.rs, который является нужным только при условии, что возможности postgres и tools были включены? Раньше нам приходилось писать что-то вроде этого:


#[cfg(not(all(feature = "postgres", feature = "tools")))]
fn main() {
    println!("This tool requires the `postgres` and `tools` features to be enabled.");
}

#[cfg(all(feature = "postgres", feature = "tools"))]
fn main() {
    // код
}

Слишком много шаблонного кода для работы с cargo build. Еще печальнее дело обстояло с examples/, которые должны демонстрировать, как использовать вашу библиотеку. Но такие махинации возможны только при работе внутри самого пакета, поэтому вы потерпите неудачу, если попытаетесь использовать пример вне этого пакета.


С помощью нового ключа required-features мы можем добавить следующее:


[[bin]]
# ...
required-features = ["postgres", "tools"]

Теперь cargo build будет собирать наш postgres-tool, только если у нас включены две эти возможности, и поэтому мы можем написать нормальный fn main без всяких нагромождений вроде cfg.


Подробнее смотрите примечания к выпуску.


Разработчики версии 1.17.0


Много людей внесли свой вклад в создание Rust 1.17. Мы не смогли бы сделать это без всех вас. Спасибо!




Автор перевода: @kgv.
Благодарю vitvakatu за помощь при переводе.
Если вы заинтересовались Rust и у вас есть вопросы, присоединяйтесь!

Поделиться с друзьями
-->

Комментарии (46)


  1. Revertis
    02.05.2017 10:47
    +5

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


    1. Halt
      02.05.2017 12:28
      +4

      Могу посоветовать поглядывать в регулярно обновляющийся milestone predictions, дабы быть в курсе относительно планируемого времени интеграции фич.

      Очень жаль, что impl Trait находится в неопределенном состоянии (на то имеются веские причины). А остальное вполне движется. Я уже несколько месяцев пользуюсь VSCode + RLS и оно «просто работает».

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

      А с учетом того что все это теперь распространяется через rustup, то начать пользоваться можно в пару команд.


  1. rkuvaldin
    02.05.2017 11:23

    На нем вообще много сейчас пишут?


    1. IchigoWalker
      02.05.2017 11:44
      +4

      На HeadHunter 5 вакансий для Rust-разработчиков


      1. Halt
        02.05.2017 12:20
        +3

        И еще 15 на специализированном сайте вакансий.


  1. Ockonal
    02.05.2017 12:31
    +1

    Пытаюсь сейчас зайти на него после с++. Необычно, но нравится.
    Кстати есть вопрос. Допустим у меня происходит парсинг html-кода, который должен вернуть какое-то число.

    fn parse_page_count(document : kuchiki::NodeRef) -> i32
    {
        let parent = document.select(".bottom_info #pagination p").unwrap().nth(0).unwrap();
        let firstBold = parent.as_node().select("b").unwrap().nth(1).unwrap();
        let result     = firstBold.as_node().as_text().unwrap().borrow().parse::<i32>();
    
        match result
        {
            Err(_)         => panic!("Failed to parse page count"),
            Ok(pagesCount) => pagesCount
        }
    }
    


    Как вообще правильно и принято в Rust оборачивать все это в проверкии и возвращение ошибки? Как использовать оператор "?" вместо кучи unwrap? Какой возвращаемый тип должен быть у функции (понимаю, что Result, но как объединить для всего).

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


    1. snuk182
      02.05.2017 12:41
      +3

      Как вообще правильно и принято в Rust оборачивать все это в проверкии и возвращение ошибки?

      С помощью Result.
      Описание самого принципа обработки ошибок (тут требуется поправка — вместо макроса try! используется сахар ?, принцип действия точно такой же)


      1. Ockonal
        02.05.2017 12:46

        Это поможет, если оборачиваемые методы возвращают одну и ту же сигнатуру Result, оно потом пробрасывается вверх. А в случае как на моем примере, там тип Err отличается.


        1. snuk182
          02.05.2017 12:52

          Ниже ответил с кодом.


        1. Halt
          02.05.2017 13:00
          +1

          Посмотрите на фрагмент, приведенный ниже или на полный вариант в окрестности строки 152.

          impl ServerState {
              pub fn parse(&mut self, message: &str) -> Result<(), &'static str> {
                  for (k, v) in parse_table(&message) {
                      match k {
                          "frame" => self.frame = Rc::new(Frame::parse_from(v)?),
          
                          "deaths" => self.deaths = v[1 .. v.len()-1] // strip quotes
                              .split(',')
                              .filter(|e| e.len() > 0) // a,,b
                              .map(|x| x.parse().map_err(|_| "parse error"))
                              .collect::<Result<_,_>>()?,
          
                          // TODO
                          _ => unimplemented!()
                      }
          
                  }
          
                  Ok(())
              }
          
              // ...
          }
          


          Из функции я выбрасываю колхозную ошибку &'static str, ибо лень и прототип. Фукнции парсинга возвращают нормальные ошибки, которые надо привести к строке.

          Самое интересное происходит в обработке «deaths»: с помощью функциональной магии и трансформации монад мы преобразуем Collection<Result<T, E>> в Result<Collection, E>>, который потом разворачиваем с помощью ?.


    1. Halt
      02.05.2017 12:46
      +3

      Если опустить детали реализации, то `?` просто выбрасывает ошибку из функции. Нормальное же значение «проглатывается» на текущем лексическом уровне. Подробнее можно посмотреть в документации.

      Если у вас есть операция, которая возвращает к примеру io::Result, а ваша функция определяет свой тип ошибки, то к вашим услугам метод map_err().


    1. snuk182
      02.05.2017 12:52
      +6

      Пример из жизни:

      fn create_player(player: &Player, tattva: Alb<Tattva>, storage: Al<Storage<Player, PlayerParams>>, idf: Al<IdFactory>) -> Result<Player, PlayerInitializeError> {
      	let universe = get_universe(player.uid)?; // <- get_universe возвращает Result<Universe, PlayerInitializeError>. Если она вернула error, create_player тоже его возвращает тут же. Похоже на проброс исключения в Java.
      	let mut player = player.clone();
      	player.id = idf.write().unwrap().player();	
      	let params = PlayerParams {
      		universe: universe.name.clone(),
      		lookup: PlayerParamsLookup::Id(player.id),
      	};	
      	storage.write().unwrap().save(&player, ¶ms).map_err(|e| {
      		error!("[{}] Error saving player {} '{}': {}", SCENE_NAME, player.id, player.name, e);
      		PlayerInitializeError::CannotSave
      	})?; // save возвращает Result<(), StorageError>. StorageError нельзя вернуть вместо PlayerInitializeError, поэтому мы его преобразовываем с помощью map_err, а заодно и логгируем. Можно враппер написать, но обычно незачем, если все логгируется.	
      	Ok(player)
      }
      


      1. Ockonal
        02.05.2017 13:03
        +1

        Спасибо, а вопрос по поводу цепочки вызовов?
        Вот пока не совсем понимаю где можно использовать clone, где вот эти into. Даже в вашем примере, насколько правильно создавать копию игрока.
        А так крутой язык, всем советую попробовать. Сначала казался бесполезным и не стоящим своего изучения.
        Но вот если разработчики еще собираются работать над упрощением порога входа, то должно быть вообще круто.
        Им бы еще поработать в направлении веба, чтобы было удобно писать бекенд. Понимаю, что это не ниша языка изначально, но сейчас, например, переписываю для своего сайта с аудиокнигами парсеры на Rust, все это мультипоточно в виде демона, было бы удобно использовать одни и те же структуры парсеров и бекенда сайта.


        1. snuk182
          02.05.2017 13:09
          +3

          Отвечу сразу про веб — Rocket наше все. Плюс по мелочи — Diesel, Tokio


          1. Halt
            02.05.2017 13:11
            +2

            А как же Serde?


          1. Ockonal
            02.05.2017 13:13
            +1

            Мне скоро нужно будет заняться одним очень масштабным мероприятием, где буду реализовывать систему электронных денег, оплаты, входа и и т.д. Хочу попробовать сделать бекенд на расте. Вот здесь его стабильность и строгая типизация может очень помочь от глупых ошибок. На librocket и смотрел :)


          1. Ockonal
            02.05.2017 13:18

            По поводу первого вопроса чуть разверну:

            let parent = document.select(".bottom_info #pagination p").unwrap().nth(0).unwrap();
            let firstBold = parent.as_node().select("b").unwrap().nth(1).unwrap();
            let result    = firstBold.as_node().as_text().unwrap().borrow().parse::<i32>();
            

            Почему код выше работает, а если объединить в одну строчку:
            let firstBold = parent.as_node().select("b").unwrap().nth(1).unwrap().as_node().as_text().unwrap().borrow().parse::<i32>();
            

            То матерится вот так


            1. Sirikid
              02.05.2017 13:47
              +4

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


            1. splav_asv
              02.05.2017 14:00

              Предположу, что первая строка создает некий объект, а в процессе получения result его часть заимствуют. Таким образом в первом случае result корректен до того момента, пока parent не выйдет из области видимости. Для однострочника parent создается только как временный и разрушается по окончании данной строки. Получается не у кого заимствовать.
              Формально rust прав. Если временный объект не доживает до конца области видимости, можно получить подвисший указатель и долго искать в чем плавающий баг.


              1. Ockonal
                02.05.2017 14:06

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


                1. splav_asv
                  02.05.2017 14:09

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


                1. splav_asv
                  02.05.2017 14:12
                  +3

                  P.S. cargo check иногда сильно экономит время, если еще не пользуетесь.


                  1. Ockonal
                    02.05.2017 14:14
                    +1

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


                    1. BlessMaster
                      02.05.2017 23:34
                      +3

                      Да, и чем дальше в лес, чем крупнее проект, тем это качество всё важнее.
                      При этом попутно происходит ещё и обучение — почему так или иначе делать нельзя или просто плохо в других языках, у которых нет такого строгого контроля.
                      А с опытом эта «борьба» с компилятором проходит, начинаешь чётко представлять как всё будет работать, и эти ошибки из-за непонимания перестают отнимать время.
                      Кстати, очень рекомендовал бы сразу изучить вдоль и впоперек стандартную библиотеку — она минималистична и это не отнимет много времени, зато многие "тупиковые" вопросы, когда непонятно что делать, будут сняты.


            1. googol
              10.05.2017 05:33
              +2

              Блогпост по теме объясняющий проблему http://manishearth.github.io/blog/2017/04/13/prolonging-temporaries-in-rust/


              1. splav_asv
                10.05.2017 10:18
                +1

                По ссылке есть малоизвестная интересная фича, по крайней мере я не знал.
                let owned; //
                let maybe_foo = if some_condition {
                thing.get_ref()
                } else {
                owned = thing.get_owned();
                owned.as_ref()
                };


                1. googol
                  10.05.2017 17:27
                  +1

                  Блог http://manishearth.github.io/blog/ в целом очень информативен. Крайне рекомендую к прочтению вот эти статьи

                  http://manishearth.github.io/blog/2015/05/17/the-problem-with-shared-mutability/
                  http://manishearth.github.io/blog/2015/05/27/wrapper-types-in-rust-choosing-your-guarantees/
                  http://manishearth.github.io/blog/2015/05/30/how-rust-achieves-thread-safety/
                  http://manishearth.github.io/blog/2017/04/13/prolonging-temporaries-in-rust/
                  http://manishearth.github.io/blog/2017/01/10/rust-tidbits-box-is-special/


          1. c0va23
            03.05.2017 01:31

            Но rocket пока работает только на ночных сборках. В продакшен такое пускать не очень хочется. Хотя выглядит он очень вкусно.


            1. Arekusei
              04.05.2017 14:12

              А что именно Rocket требует из ночных сборок?
              Если я правильно понял то он нуждается в плагинах для кодогенерации, да?
              А вообще есть какие-то планы у Rust по стабилизации данных фич?


              1. ozkriff
                04.05.2017 14:14
                +1

                https://github.com/SergioBenitez/Rocket/blob/30fac3297/lib/src/lib.rs#L1-L10


                довольно много всего:


                #![feature(specialization)]
                #![feature(conservative_impl_trait)]
                #![feature(drop_types_in_const)]
                #![feature(associated_consts)]
                #![feature(const_fn)]
                #![feature(type_ascription)]
                #![feature(lookup_host)]
                #![feature(plugin)]
                #![feature(never_type)]
                #![feature(concat_idents)]


                1. Arekusei
                  04.05.2017 14:22

                  Да, много фич которые ещё непонятно когда и в каком виде попадут в stable.


        1. snuk182
          02.05.2017 13:48
          +3

          Clone — это клонирование. Явная копия объекта (неявная — это Copy, но от Copy на практике больше мороки, чем пользы). Может быть не так тривиальна, если клонируемый тип имеет поля-ссылки.
          Into/From — паттерн Adapter из ООП, преобразует одни типы в другие, заодно является полу-сахаром для компилятора, если надо использовать (вернуть из функции, передать в функцию) объект, несовместимый с сигнатурой, но зато имеющий реализацию Into или From. Но тоже явно, методы из типажей надо вызывать вручную.


          1. splav_asv
            02.05.2017 14:01
            +1

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


            1. snuk182
              02.05.2017 14:23

              Да, для ссылок — Borrow(Mut) и AsRef/AsMut.


          1. splav_asv
            02.05.2017 14:06
            +1

            Но да, для From и Into явное, прошу прощения, слегка невнимательно прочитал.


            1. BlessMaster
              02.05.2017 23:38

              На самом деле уточню, что для ошибок в случае try! / ? — вызов .into() происходит «под капотом», что сильно упрощает жизнь, да и в целом, макросы немного нивелируют принцип явности.


        1. Halt
          04.05.2017 13:39
          +4

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

          При чтении можно мысленно заменять try!() на ?, смысл будет тот же.


          1. Ockonal
            04.05.2017 13:40

            Спасибо, я уже наловчился со всеми мапперами ошибок. Главное найти грань, чтобы из трех строк кода не полчилось 30 :)


            1. Halt
              04.05.2017 13:42
              +2

              Так там наоборот из 30 получится 3. Реализация трейтов преобразования поможет автоматически захватывать ошибки разных типов вообще без map_err(). Просто пишете ?, а оно само разберется.


      1. pftbest
        02.05.2017 22:02
        +2

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


  1. GraD_Kh
    10.05.2017 21:46
    +1

    Начал разбираться с растом, и удивился и порадовался его продуманности во многих вещах — контроль времени жизни ссылок, невозможность использовать после move, функциональные возможности. Но очень расстраивает отсутствие корутин и N:M-параллелизма. Даже аналога обычного C++-ного std::async нет из коробки, да и futures какие-то странные. Мне кажется, что если бы это было, то rust мог бы серьезно побороться за нишу серверныз приложений, а пока шансов, увы, немного.
    Насколько я понял, была годная реализация корутин, но она оказалась несовместимой со стандартной библиотекой из-за того, что последняя активно использует TLS. И на данный момент корутины в принципе не в приоритете. Было бы хорошо, если бы кто-то внес уточнения, вдруг я что-то упустил.


    1. TargetSan
      10.05.2017 22:55
      +2

      Для начала, std::async из с++ это просто потоковая функция с гардом, на котором можно подождать окончания и получить результат. Полный аналог https://doc.rust-lang.org/std/thread/fn.spawn.html. Если хотите полноценной асинхронности — https://tokio.rs/


      1. GraD_Kh
        11.05.2017 09:47

        Я в курсе, что такое std::async. И если быть точным, то она не всегда создает новый поток. Во-первых в зависимости от дефолтной политики результат может быть получен вообще синхронно. Во-вторых, в зависимости от имплементации асинхронная версия может либо создавать новый поток, либо использовать пул потоков.
        За наводку на https://tokio.rs/ — спасибо.


        1. TargetSan
          11.05.2017 11:01
          +1

          Вот в этом и проблема, что смешали в кучу 2 разных инструмента, добавив столь любимую Комитетом горстку implementation-defined. Нет, даже 3. Потому что запуск в эксклюзивном потоке и на тред пуле — две большие разницы.


          1. TargetSan
            11.05.2017 11:29
            +1

            Хотя нет, если http://en.cppreference.com/w/cpp/thread/async ничего не упускает — всегда или новый поток, или ленивое значение. Однако это всё равно крайне плохое решение. Во-первых, это таки смешивание двух совершенно разных вещей в одну кучу. Во-вторых, что гораздо хуже, выбор производится флагами (!), да к тому же если указаны оба флага, выбор implementation-defined (!!!). В общем грусть-печаль от такой стандартной библиотеки.


            1. GraD_Kh
              11.05.2017 12:35

              Да, похоже, что thread pool не используется. Я был сбит с толку этой темой. Причем похоже не используется как раз из-за потенциальных проблем с TLS.