rust macros: ident and ty are conflicting with each other - rust

macro_rules! thisis {
($t: ty) => {
println!("this is a type")
};
($t: ident) => {
println!("this is an ident")
};
}
fn main()
{
const variable: i32 = 32;
thisis!(i32);
thisis!(variable);
}
in the example above it prints "this is a type", in both cases, it doesn't really recognize the difference between types and idents. it goes with which ever difinition comes first.

Macros are not context sensitive. Your macro doesn't know whether a given name is a variable or a type in the current scope. It only knows what it can glean from the context-free syntax of the language, and by those rules both i32 and variable are valid type and/or identifier names.
Specifically, the Rust reference defines ty and ident as follows.
ty: a Type
ident: an IDENTIFIER_OR_KEYWORD or RAW_IDENTIFIER
Where Type is defined as several things, but one of those is TypePath, which is effectively any string of identifiers connected by ::.
IDENTIFIER_OR_KEYWORD, as the name implies, is any valid identifier name in Rust, or a Rust keyword. RAW_IDENTIFIER is an identifier (including a keyword) prefixed with r# (this allows us to, for instance, use the word if as a variable name by writing it as r#if
So ordinary identifiers like foo are both ty and ident by Rust's syntax rules. Qualified names like foo::bar are only ty and not ident. Meanwhile, a language keyword like while or if (written without the raw identifier r# prefix) would be an ident but not a ty.
You just don't have this level of semantic information at the macro level, unfortunately.

Related

Is there a way to capture the parameter types of a function definition with a macro?

Ultimately, I'd like to be able to wrap a function in a proc_macro like this:
native_function! {
fn sum(x:i32, y:i32) -> i32 {
x+y
}
}
I'm trying to find what I need to put in this function:
#[proc_macro]
pub fn native_function(item: TokenStream) -> TokenStream {
...
}
Inside the function above, I need to be able to capture the types of the parameters defined in the function; in this case - "i32"
I've tried all sorts of ways to do this, but currently I'm stumped. I'm trying to avoid using a derive macro since I don't want to have to complete and instantiate a struct. I don't even need a full solution, but if someone can point me to the function/object/library I need to be using to that would be great.
syn is the cannonical parsing library for proc macros.
Using it this is easy:
let f = syn::parse_macro_input!(item as syn::ItemFn);
let types: Vec<syn::Type> = f
.sig
.inputs
.into_iter()
.filter_map(|arg| match arg {
syn::FnArg::Receiver(_) => None,
syn::FnArg::Typed(syn::PatType { ty, .. }) => Some(*ty),
})
.collect();
By the way, your macro can be an attribute macro.
If you want to know whethere the type is a some known type, note first that you can never be sure; that is because macros operate without type information, and code like struct i32; is legal and will shadow the primitive i32 type.
If you've accepted this limitation, what you actually want is to compare it to a path type. Path types are represented by the Path variant of syn::Type. It has qself, which is the T in <T as Trait>::AssociatedType and None in a simple path (not fully-qualified, just A::B::C), and path, which is the Trait::AssociatedType or A::B::C. Paths are complex; they can contain generics, for example, but if all you want is to check if this is one-segment type of simple identifier like i32, syn has got you covered: just use Path::is_ident(). So:
let is_i32 = match ty {
syn::Type::Path(ty) => ty.path.is_ident("i32"),
_ => false,
}
If you want to compare against a more complex type, you will have to walk and match segment-by-segment.

Is it possible to have a macro for a hashmap literal with "key: value" syntax?

This maplit crate allows hashmap literals with => as a separator. I believe it's impossible to use macro_rules! to allow the : separator, but is it possible with a macro that processes a stream of tokens?
Is it impossible to add { key: value } style macros to the language, even in the compiler?
I suspect the problem is conflict with the : type operator, but I cannot construct an ambiguous example where it is impossible for the compiler to make a decision of which way to interpret :.
#kmdreko does a good job of explaining why you can't have an expression followed by a colon in a macro. However, you can work around that, by forcing the key to be a token tree rather than an expression:
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 ()) ),*]) }
}
playground
The drawback of taking this route, as opposed to a procedural macro, which probably could handle this pattern just fine, is that most complex expressions will have to be wrapped in parentheses or curly brackets. For example,
let map = hashmap!{ 2 + 2 : "foo" };
won't work, but
let map = hashmap!{ (2 + 2) : "foo" };
will.
If you were to replace the => with a : in the maplit! macro you'd get this error:
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 `;`
The specific problem is that the macro accepts any arbitrary expression as the key type. You could still have the { key: value } syntax, but only if the key is an ident (for example) instead of an expr.
You can read the Follow-set Ambiguity Restrictions section of Macros by Example in the 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 and stmt may only be followed by one of: =>, ,, or ;.
That's not to say its necessarily ambiguous, though it may get confusing to parse in examples with a lot of :s like MyEnum::Variant: ::std::collections::Vec::new(). Its designed like that because only the documented tokens are guaranteed to indicate the end of an expression and other tokens may become ambiguous in the future.
You'll see though, that this only limits how macro_rules! patterns are handled and would not affect a procedural macro since you have free reign to decide how the tokens are parsed.
I tried to create a simple procedural macro using syn to aid in parsing expressions, but it chokes on the Expr : Expr syntax because it attempts to eagerly parse the first expression as a type ascription expression which is a yet-to-be-completed feature. This is the kind of future expansion that the macro_rules! guards against.
Going with a token tree and using parenthesis for ambiguity in Aiden4's answer is the way to go if you really want : instead of =>. This still could be done with a procedural macro, but it would not be as readily available and would be ambiguous if type ascription expressions gets added to the language.

Leave associated type unspecified in trait implementation

I want to leave an associated type specification in a trait implementation for later, as it requires some work. Is there any way I can tell the compiler to ignore the missing associated type?
impl A for B {
type C = todo()!;
}
It throws this error
error: non-type macro in type position: ::core::panic
Rust macros like todo!() aren't magic, they must expand to valid Rust code. In the case of todo() it expands to panic!("not yet implemented: ....") (see src).
If you use a macro in a type, then it must expand to a valid type. Unfortunately, that means you can't have it expand to the equivalent of todo!(): it wouldn't really make sense!
However, instead, you can use () as a placeholder, or even define a custom type alias: type Todo = ().

Formatting a const string in Rust [duplicate]

This extremely simple Rust program:
fn main() {
let c = "hello";
println!(c);
}
throws the following compile-time error:
error: expected a literal
--> src/main.rs:3:14
|
3 | println!(c);
| ^
In previous versions of Rust, the error said:
error: format argument must be a string literal.
println!(c);
^
Replacing the program with:
fn main() {
println!("Hello");
}
Works fine.
The meaning of this error isn't clear to me and a Google search hasn't really shed light on it. Why does passing c to the println! macro cause a compile time error? This seems like quite unusual behaviour.
This should work:
fn main() {
let c = "hello";
println!("{}", c);
}
The string "{}" is a template where {} will be replaced by the next argument passed to println!.
TL;DR If you don't care why and just want to fix it, see the sibling answer.
The reason that
fn main() {
let c = "hello";
println!(c);
}
Cannot work is because the println! macro looks at the string at compile time and verifies that the arguments and argument specifiers match in amount and type (this is a very good thing!). At this point in time, during macro evaluation, it's not possible to tell that c came from a literal or a function or what have you.
Here's an example of what the macro expands out to:
let c = "hello";
match (&c,) {
(__arg0,) => {
#[inline]
#[allow(dead_code)]
static __STATIC_FMTSTR: &'static [&'static str] = &[""];
::std::io::stdio::println_args(&::std::fmt::Arguments::new(
__STATIC_FMTSTR,
&[::std::fmt::argument(::std::fmt::Show::fmt, __arg0)]
))
}
};
I don't think that it's actually impossible for the compiler to figure this out, but it would probably take a lot of work with potentially little gain. Macros operate on portions of the AST and the AST only has type information. To work in this case, the AST would have to include the source of the identifier and enough information to determine it's acceptable to be used as a format string. In addition, it might interact poorly with type inference - you'd want to know the type before it's been picked yet!
The error message asks for a "string literal". What does the word "literal" mean? asks about what that means, which links to the Wikipedia entry:
a literal is a notation for representing a fixed value in source code
"foo" is a string literal, 8 is a numeric literal. let s = "foo" is a statement that assigns the value of a string literal to an identifier (variable). println!(s) is a statement that provides an identifier to the macro.
If you really want to define the first argument of println! in one place, I found a way to do it. You can use a macro:
macro_rules! hello {() => ("hello")};
println!(hello!());
Doesn't look too useful here, but I wanted to use the same formatting in a few places, and in this case the method was very helpful:
macro_rules! cell_format {() => ("{:<10}")}; // Pads with spaces on right
// to fill up 10 characters
println!(cell_format!(), "Foo");
println!(cell_format!(), 456);
The macro saved me from having to duplicate the formatting option in my code.
You could also, obviously, make the macro more fancy and take arguments if necessary to print different things with different arguments.
If your format string will be reused only a moderate number of times, and only some variable data will be changed, then a small function may be a better option than a macro:
fn pr(x: &str) {
println!("Some stuff that will always repeat, something variable: {}", x);
}
pr("I am the variable data");
Outputs
Some stuff that will always repeat, something variable: I am the variable data

What is the syntax: `instance.method::<SomeThing>()`?

I read the below syntax from byteorder:
rdr.read_u16::<BigEndian>()
I can't find any documentation which explains the syntax instance.method::<SomeThing>()
This construct is called turbofish. If you search for this statement, you will discover its definition and its usage.
Although the first edition of The Rust Programming Language is outdated, I feel that this particular section is better than in the second book.
Quoting the second edition:
path::<...>, method::<...>
Specifies parameters to generic type, function, or method in an expression; often referred to as turbofish (e.g., "42".parse::<i32>())
You can use it in any kind of situation where the compiler is not able to deduce the type parameter, e.g.
fn main () {
let a = (0..255).sum();
let b = (0..255).sum::<u32>();
let c: u32 = (0..255).sum();
}
a does not work because it cannot deduce the variable type.
b does work because we specify the type parameter directly with the turbofish syntax.
c does work because we specify the type of c directly.

Resources