Я собираюсь предположить, что под «пользовательским литералом» вы подразумеваете «обычный литерал Rust (исключая необработанные литералы), за которым сразу следует пользовательский идентификатор». Это включает в себя:
"str"x
, строковый литерал "str"
с пользовательским суффиксом x
123x
, числовой c литерал 123
с пользовательским суффиксом x
b"bytes"x
, байтовый литерал b"bytes"
с пользовательским суффиксом x
Если приведенное выше определение достаточно для вас, то вам повезло, так как все вышеперечисленное действительно является действительными буквенными токенами в Rust, согласно ссылка на Rust :
Суффикс - это необработанный идентификатор сразу (без пробела) после основной части. литерала.
Любой тип литерала (строка, целое число и т. д. c) с любым суффиксом является допустимым в качестве токена и может быть передан макросу без выдачи ошибки. Сам макрос решает, как интерпретировать такой токен и выдавать ошибку или нет.
Однако суффиксы на литеральных токенах, анализируемых как код Rust, ограничены. Любые суффиксы отклоняются на нечисловых c литеральных токенах, а числовые c литеральные токены принимаются только с суффиксами из списка ниже.
Так что Rust явно позволяет макросы для поддержки пользовательских строковых литералов.
Теперь, как бы вы go написали такой макрос? Вы не можете написать декларативный макрос с macro_rules!
, так как невозможно обнаружить и манипулировать пользовательскими литералами с помощью простого сопоставления с образцом. Тем не менее, можно написать процедурный макрос , который делает это.
Я не буду go вдаваться в подробности о том, как писать процедурные макросы, так как это было бы слишком много, чтобы написать в одном ответе StackOverflow. Тем не менее, я дам вам этот пример процедурного макроса, который делает что-то в соответствии с тем, что вы просили, в качестве отправной точки. Он принимает любые пользовательские целочисленные литералы 123x
или 123y
в данном выражении и преобразует их в вызовы функций x_literal(123)
и y_literal(123)
вместо:
extern crate proc_macro;
use proc_macro::TokenStream;
use quote::ToTokens;
use syn::{
parse_macro_input, parse_quote,
visit_mut::{self, VisitMut},
Expr, ExprLit, Lit, LitInt,
};
// actual procedural macro
#[proc_macro]
pub fn vector(input: TokenStream) -> TokenStream {
let mut input = parse_macro_input!(input as Expr);
LiteralReplacer.visit_expr_mut(&mut input);
input.into_token_stream().into()
}
// "visitor" that visits every node in the syntax tree
// we add our own behavior to replace custom literals with proper Rust code
struct LiteralReplacer;
impl VisitMut for LiteralReplacer {
fn visit_expr_mut(&mut self, i: &mut Expr) {
if let Expr::Lit(ExprLit { lit, .. }) = i {
match lit {
Lit::Int(lit) => {
// get literal suffix
let suffix = lit.suffix();
// get literal without suffix
let lit_nosuffix = LitInt::new(lit.base10_digits(), lit.span());
match suffix {
// replace literal expression with new expression
"x" => *i = parse_quote! { x_literal(#lit_nosuffix) },
"y" => *i = parse_quote! { y_literal(#lit_nosuffix) },
_ => (), // other literal suffix we won't modify
}
}
_ => (), // other literal type we won't modify
}
} else {
// not a literal, use default visitor method
visit_mut::visit_expr_mut(self, i)
}
}
}
Макрос, например, преобразует vector!(3x + 4y)
в x_literal(3) + y_literal(4)
.