Pull to refresh

Comments 46

Новый прогрессбар так себе помещается в узкие вертикальные консоли по 90-100 символов, но не могу найти опций cargo для его отключения. Никто не натыкался?

Похоже, что не отключается. Придется терпеть )
Хотя у меня нормально отображается и в узкой консоли:
image

Да, походу в совсем узкой консоли имена компилируемых пакетов не показываются и все окей. А вот если сделать 90-100 шириной, то справа от индексов компилируемых пакетов показываются их имена и они уже не влезают в границу строки нормально. :(

UFO just landed and posted this here
А в чем, собственно, проблема? Атрибуты в Rust реализованы не через строки, как в Go, они вполне себе парсятся и проверяются во время компиляции.
UFO just landed and posted this here
На сколько я понимаю, аналогом данной конструкции в python являются декораторы. Если так, то вы безусловно не правы. При правильном применении, декораторы способны сделать ваш код проще, главное не перебарщивать с их использованием.
UFO just landed and posted this here

Давайте предположим, что здесь кто-нибудь не разбирается в теме — расскажите пожалуйста, чем это небезопасно?

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

Ну так это же user(library)-defined правила, а не вшитые в язык. Не хотите — не пользуйтесь, генерируйте роуты ручками.


Сравните подход go vs rust. Только угоротый подтвердит, что в go аннотирование структур через строки (которые проверяются только в рантайме) сделаны удачно.

Оооккк. Где записываться в "угоротые"?


*Мне кажется, вы тоже несколько слишком категоричны.

Если вы системным программированием зовете скудность языка C, то это исключительно ваши проблемы восприятия мира системного программирования.
Несмотря на то, что Rust позиционируется как системный язык программирования, он также является языком программирования общего назначения. Что плохого в том, что как системный ЯП Rust имеет развитую систему типов и довольно высокоуровневые абстракции с нулевой стоимостью, а как прикладной ЯП — высокую производительность?
Тут скорее у человека связь «низкоуровенвый язык == обязательно страдать». У многих замечал, видимо, по историческим причинам.
Видимо при получении так называемого образования им навязали выученную беспомощность.
Почему? Мне ассемблер нравился в своё время. Давно, правда, с ним не сталкивался, но страданий не испытываю.
Системное программирование != ассемблер.
Да, но речь-то шла о
«низкоуровенвый язык == обязательно страдать»
Вообще, что касается правил роутинга, их удобнее иметь в отдельном файле — чтобы, например, сразу видеть конфликты -а не раскидывать по коду.

Ну вот посмотрите, как это делается в Rocket:


#[get("/<name>/<age>")]
fn hello(name: String, age: u8) -> String {
    format!("Hello, {} year old named {}!", age, name)
}

fn main() {
    rocket::ignite().mount("/hello", routes![hello]).launch();
}

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

Вы написали:
#[get("/<name>/<age>")]
а теперь представим, что в другом месте вы определили маршрут
#[get("/<model>/<year_of_product>")]
что при этом произойдет?
Если монтировать их на разные конечные точки — ничего особенного не произойдет…
«представим, что в другом месте» — а если эти два маршрута определить в одном месте, проблема ведь никуда не денется, так? Её только заметят с большей вероятностью. Проблема тут возникает из-за использования сегментов пути для передачи параметров. Это в принципе плохо и проблемы с таким подходом будут всегда, вне зависимости от технической реализации.

У URL есть разные специализированные части, en.wikipedia.org/wiki/URL#Syntax. Для передачи агрументов запроса служит «query», а в этом примере мы пытаемся передавать их через «path». «Path» предназначен для описания иерархичного пути (изначально соответствовал пути файла в ФС), к примеру, Application/Controller/Method. Такой путь практически гарантированно будет уникальным (1 путь = 0..1 файлов / методов). А в примере мы передаем в сегментах аргументы, для которых уникальность не является условием, отсюда и проблема с определением правильного маршрута.
Как практик, отвечаю, в Rocket для этого есть аттрибут «rank».

Ничего хорошего в нём нет, но приходится делать, то, что приходится :)
Не всегда получится сделать так, что у роутов будут разные префиксы. И даже если они разные, все равно, в реальном коде (не с функциями на 3 строчки как у вас), эти роуты будут далеко друг от друга раскиданы.
в других языках

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

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

Вы про .htaccess сейчас?
Я про файл с конфигурацией роутинга, вроде routing.yml из Симфони.
Кстати, чтение, разбор и генерацию кода по такому файлу тоже можно сделать на этапе компиляции с помощью процедурных макросов.
Частично соглашусь.

Эти маршруты действительно красиво выглядят в документации (и в подходящих проектах, наверное), но у нас в итоге всё свелось к фактически одному маршруту на всё подряд (ну, к 4-ем, GET/POST/PUT/DELETE), а дальше мы запрос сами разбираем… (тут возникает справедливый вопрос, зачем нам Rocket, но просто пока времени нет всё на hyper переделать).

Но всё же для разных задач — разные инструменты. Охотно допускаю, что где-то такие маршруты (и Rocket) могут быть удобны. Для более фиксированных API (наша проблема в том, что API очень уж динамичные).
Атрибуты для функций привычное дело во многих языках.
К примеру роутинги аггрегируются и с помощью утилит предоставляемых фрейморком отлаживать их не проблема.
Те ещё пляски с бубном вокруг модулей, да методов именований. Лучше бы о массивах фиксмрованной длинны подумали.

Помню, не мог разобраться как макросы импортировать, мне нравятся сегодняшние изменения.
А массивы, что с ними не так?

А массивы, что с ними не так?
Проблема 1: Массив фиксированной длины нельзя ициниализировать в цикле или, скажем, из среза или итератора. Например, если вы хотите заполнить массив длины N (где N — констана) числами от 0 до N-1, вам придется написать что-то вроде

let a = [0i32; N];
for i in 0..N {
    a[i] = i;
}

Здесь память переписывается дважды: первый раз нулями при инициализации массива, второй раз при заполнении числами. Нет никакой возможности этого избежать.

Проблема 2: Длину фиксированного массива нельзя сделать параметром шаблона. В C++ вы можете написать
template<size_t N>
struct Foo {
    int x[N];
};
В текущей версии Rust числа не могут быть параметрами шаблона, поэтому так сделать нельзя. Из этого вытекают многие другие проблемы. Например, нельзя реализовать какое-либо свойство (trait) для массивов произвольной длины. Если вы откроете страницу документации о массивах, то увидите, что реализации стандандартных свойств генерируются с помощью макросов для каждого N от 1 до 32: doc.rust-lang.org/std/primitive.array.html (в самом конце страницы). То есть, например, PartialEq реализовано для [u8; 32], но не для [u8; 33].

В unsafe Rust первая проблема решается:


let a = unsafe {
    let mut array: [i32; N] = std::mem::uninitialized();
    for i in 0..N {
        std::ptr::write(&mut array[i], i);
    }
    array
};
Во многих случаях первая проблема и оптимизатором решается. godbolt.org/z/3HHGYt

Но при длине массива 52 и больше оптимизатор почему-то сдаётся. Так что с unsafe всё-таки надёжнее.

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

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

Если вам не обязательно хранить ссылку на родителя в дочернем элементе, то реализация будет тривиальна. В противном случае вам придется:


  • Использовать Rc, RefCell и прочие Weak; или
  • Использовать сырые указатели и управлять освобождением памяти вручную; или
  • Хранить узлы во внешнем контейнере и ссылаться на них по индексу; или
  • Использовать готовые библиотеки, которые скрывают всю кухню за своим API.

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

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

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

Спасибо большое! Изучу.

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


Допустим у вас есть некий объект и вы хотите хранить ссылки на него в нескольких местах одновременно. Но тут возникает сразу две проблемы:


  1. Объект должен быть доступен из разных мест по ссылке, и удален только тогда, когда на него нет ссылок. Если есть по крайней мере одна ссылка, то объект должен жить.
  2. Правила Rust позволяют иметь больше одной ссылки только на неизменяемый объект. Ссылка на изменяемый объект может быть только одна, без каких либо других ссылок на этот объект.

Первая проблема решается подсчетом ссылок (reference counting) и в других популярных прикладных ЯП она обычно скрыта от пользователя за реализацией. То есть любая "ссылка" на объект в таких ЯП всегда есть умный указатель со счетчиком ссылок. В Rust же такого типа ссылку нужно создавать вручную, если она вам нужна. Для этого используются типы Rc и Arc. Кроме того, нужно как-то решать вопрос образования циклических ссылок (Weak).


Вторая проблема решается введением совместно используемых изменяемых контейнеров — это типы Cell, RefCell, Mutex, RwLock.


Я рекомендую прочитать вот эту страницу официальной документации и разобрать приведенный там пример с гаджетами для того, чтобы понять, для чего и как использовать Rc, RefCell и Weak вместе. Также рекомендую ознакомиться с документацией по cell.

Большое спасибо за разъяснения!
Sign up to leave a comment.

Articles