Этот крейт маплит позволяет использовать литералы хэш-карт с =>
в качестве разделителя. Я считаю, что невозможно использовать macro_rules!
, чтобы разрешить разделитель :
, но возможно ли это с помощью макроса, который обрабатывает поток токенов?
Нельзя ли добавить в язык макросы стиля { key: value }
, даже в компиляторе?
Я подозреваю, что проблема в конфликте с оператором типа :
, но я не могу построить неоднозначный пример, в котором компилятор не может принять решение о том, как интерпретировать :
.
Если бы вы заменили =>
на :
в макросе maplit!
, вы бы получили эту ошибку:
error: `$key:expr` is followed by `:`, which is not allowed for `expr` fragments
--> src/lib.rs:6:17
|
6 | ($($key:expr: $value:expr),*) => {
| ^ not allowed after `expr` fragments
|
= note: allowed there are: `=>`, `,` or `;`
Конкретная проблема заключается в том, что макрос принимает любое произвольное выражение в качестве типа ключа. Вы все еще можете использовать синтаксис { key: value }
, но только если key
является ident
(например) вместо expr
.
Вы можете прочитать раздел Последующая установка ограничений неоднозначности макросов на примерах в Rust Reference:
The parser used by the macro system is reasonably powerful, but it is limited in order to prevent ambiguity in current or future versions of the language.
[...]
expr
andstmt
may only be followed by one of:=>
,,
, or;
.
Это не означает, что это обязательно неоднозначно, хотя может возникнуть путаница при анализе примеров с большим количеством :
, таких как MyEnum::Variant: ::std::collections::Vec::new()
. Он разработан таким образом, потому что только документированные токены гарантировано указывают на конец выражения, а другие токены может стать неоднозначны в будущем.
Однако вы увидите, что это только ограничивает обработку шаблонов macro_rules!
и не повлияет на процедурный макрос, поскольку вы можете свободно решать, как анализировать токены.
Я попытался создать простой процедурный макрос, используя syn
, чтобы помочь в анализе выражений, но он задыхается от синтаксиса Expr : Expr
, потому что он пытается жадно проанализировать первое выражение как выражение приписывания типа, которое является еще не завершенным характерная черта. Это своего рода будущее расширение, от которого защищает macro_rules!
.
Использование дерева токенов и использование скобок для двусмысленности в Ответ Эйден4 — это то, что нужно, если вы действительно хотите :
вместо =>
. Это по-прежнему можно сделать с помощью процедурного макроса, но он будет не так легко доступен и будет неоднозначным, если в язык будут добавлены выражения приписывания типов.
Мой вопрос был о процедурных макросах. Это определенно возможно с процедурными макросами?
@Test Я отредактировал свой ответ, но вывод немного неудовлетворителен.
@kmdreko хорошо объясняет, почему в макросе не может быть выражения, за которым следует двоеточие. Однако вы можете обойти это, заставив ключ быть деревом токенов, а не выражением:
macro_rules! hashmap{
( $($key:tt : $val:expr),* $(,)? ) =>{{
#[allow(unused_mut)]
let mut map = ::std::collections::HashMap::with_capacity(hashmap!(@count $($key),* ));
$(
#[allow(unused_parens)]
let _ = map.insert($key, $val);
)*
map
}};
(@replace $_t:tt $e:expr ) => { $e };
(@count $($t:tt)*) => { <[()]>::len(&[$( hashmap!(@replace $t ()) ),*]) }
}
Недостаток этого пути, в отличие от процедурного макроса, который, вероятно, прекрасно справляется с этим шаблоном, заключается в том, что самые сложные выражения должны быть заключены в круглые или фигурные скобки. Например,
let map = hashmap!{ 2 + 2 : "foo" };
не сработает, но
let map = hashmap!{ (2 + 2) : "foo" };
будем.
Удивительно. Я предложил добавить это в maplit.
Это действительно невероятно. Я думаю, что это так же, как, например, макрос json!()
в serde-json, но все же круто видеть написанное объяснение.
@rv.kvetch макрос json!()
использует гораздо более сложный tt muncher, который может анализировать expr : expr
и сразу же ломается всякий раз, когда присваивание типов для выражений становится вещью.
Скорее всего, вы сможете добиться того, чего хотите, с помощью функционально-подобный процедурный макрос.