Комментарии 46
Новый прогрессбар так себе помещается в узкие вертикальные консоли по 90-100 символов, но не могу найти опций cargo для его отключения. Никто не натыкался?
Хотя у меня нормально отображается и в узкой консоли:
![image](https://habrastorage.org/webt/r-/d4/38/r-d438acryojbiyxees2-h8cpj4.jpeg)
Давайте предположим, что здесь кто-нибудь не разбирается в теме — расскажите пожалуйста, чем это небезопасно?
Что же касается самого шаблона «декоратор», я прекрасно знаю о его плюсах и минусах, но готов выслушать ваше мнение. Или вы больше против экспериментов, как таковых, в языках программирования?
Но я грешным делом думал, что системный язык программирования, ориентированный на безопасность и применение во всяких там эмбедах — не место для подобных экспериментов.
Ну так это же user(library)-defined правила, а не вшитые в язык. Не хотите — не пользуйтесь, генерируйте роуты ручками.
Сравните подход go vs rust. Только угоротый подтвердит, что в go аннотирование структур через строки (которые проверяются только в рантайме) сделаны удачно.
Ну вот посмотрите, как это делается в 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.rs вполне себе есть живые ржавые альтернативы.
Ну и что с того? У вас же монтироваться они будут в одном месте — переходите к указанной функции-обработчику и все. Наоборот, удобно, что та часть запроса, которая должна соответствовать сигнатуре обработчика, указывается рядом с этой сигнатурой. Более того, так как она задается в процедурном макросе, мы можем проверять во время компиляции это соответствие.
Эти маршруты действительно красиво выглядят в документации (и в подходящих проектах, наверное), но у нас в итоге всё свелось к фактически одному маршруту на всё подряд (ну, к 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
};
Но при длине массива 52 и больше оптимизатор почему-то сдаётся. Так что с unsafe всё-таки надёжнее.
Ну сейчас устаканивается Rust 2018, поэтому в основном завершается стабилизация уже разработанного ранее. Константы в обобщенных типах еще требуют серьезной разработки и широкого тестирования, так что в нынешнем году мы их точно не увидим в стабильном Rust.
Вопрос специалистам по Rust — пытаюсь построить дерево DOM, есть ли какие-то стандартные подходы, просто для обучения? Все библиотеки, что находил, основываются на cell и трёхэтажных шаблонах. Нет ли чего-нибудь попроще, пусть и не настолько универсального.
Если вам не обязательно хранить ссылку на родителя в дочернем элементе, то реализация будет тривиальна. В противном случае вам придется:
- Использовать
Rc
,RefCell
и прочиеWeak
; или - Использовать сырые указатели и управлять освобождением памяти вручную; или
- Хранить узлы во внешнем контейнере и ссылаться на них по индексу; или
- Использовать готовые библиотеки, которые скрывают всю кухню за своим API.
Вообще, для лучшего понимания, рекомендую изучить реализацию rust-forest
. Там представлено несколько подходов и они довольно неплохо прокомментированы.
Можно, правда, добавлять узел в дерево только после обхода всех его потомков, это снимает проблему, но, боюсь, такое подойдёт не во всех случаях.
Вообще, для лучшего понимания, рекомендую изучить реализацию rust-forest. Там представлено несколько подходов и они довольно неплохо прокомментированы.
Спасибо большое! Изучу.
В Cell-ах и трехэтажных шаблонах все же лучше сразу разобраться, чтобы понимать, как реализовывать некоторые, привычные по другим языкам программирования вещи в Rust.
Допустим у вас есть некий объект и вы хотите хранить ссылки на него в нескольких местах одновременно. Но тут возникает сразу две проблемы:
- Объект должен быть доступен из разных мест по ссылке, и удален только тогда, когда на него нет ссылок. Если есть по крайней мере одна ссылка, то объект должен жить.
- Правила Rust позволяют иметь больше одной ссылки только на неизменяемый объект. Ссылка на изменяемый объект может быть только одна, без каких либо других ссылок на этот объект.
Первая проблема решается подсчетом ссылок (reference counting) и в других популярных прикладных ЯП она обычно скрыта от пользователя за реализацией. То есть любая "ссылка" на объект в таких ЯП всегда есть умный указатель со счетчиком ссылок. В Rust же такого типа ссылку нужно создавать вручную, если она вам нужна. Для этого используются типы Rc
и Arc
. Кроме того, нужно как-то решать вопрос образования циклических ссылок (Weak
).
Вторая проблема решается введением совместно используемых изменяемых контейнеров — это типы Cell
, RefCell
, Mutex
, RwLock
.
Я рекомендую прочитать вот эту страницу официальной документации и разобрать приведенный там пример с гаджетами для того, чтобы понять, для чего и как использовать Rc
, RefCell
и Weak
вместе. Также рекомендую ознакомиться с документацией по cell.
Обещанный блог-пост таки вышел, слегка с опозданием: Help test Rust 2018
Выпуск Rust 1.30