You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
It would be good to have some extra information in the documentation about how to handle r#-prefixed identifiers. For example:
Special care should be taken when using Rust reserved words(like let, match, type, more here) in an Ident. The Ident::new will not error when called with an keyword, but Rust will fail to compile any generated code that uses it.
let special_ident = Ident::new("let",Span::call_site());// Will run fine, but if this is used in generated code, the generated code will fail to compile
Instead, you can use the Ident::new_raw(currently semver exempt) to generate a Ident that will generate valid Rust code when used as an identifier
// Is this call correct? Do I need to call this with "r#let" instead.let special_ident = Ident::new_raw("let",Span::call_site());// `special_ident` is now able to be used in code generation as an identifier// it will appear prefixed by `r#` to escape the keyword.
You may be tempted to use the Ident::new with a "r#..." argument, however this will cause the compiler to crash during ?code generation?.
An alternative to using the Ident::new_raw function(and opting-in to semver exempt functionality) is to use the syn::parse_str::<Ident> function. This function also correctly handles r# identifiers. This can be combined to create a function that will generate the correct Ident instance for both valid identifiers and keywords:
let my_var_name = "let";let ident = match syn::parse_str::<Ident>(ident){Ok(ident) => ident,// A valid identifierErr(_) => // Not a valid identifier, could be a keyword.// Call `.unwrap` to panic on a never-valid identifier like "" or "123"
syn::parse_str::<Ident>(&format!("r#{}", ident)).unwrap(),};
Some background about how I ended up here
I'm trying to generate some Rust structs from a file; and the names could be rust identifiers. In my code I used the Ident::new to convert the field name into a identifier:
let ident = Ident::new(field_name,Span::call_site())
...
This generates a valid ident, but the macro that uses it won't compile when the field_name is a rust keyword(like let). My first attempt to fix this involved this:
However using this code causes the compiler to crash during the build.
thread 'rustc' panicked at '`"r#type"` is not a valid identifier', src\librustc_expand\proc_macro_server.rs:329:13
stack backtrace:
0: <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt
1: core::fmt::write
2: <std::io::IoSlice as core::fmt::Debug>::fmt
3: std::panicking::take_hook
4: std::panicking::take_hook
5: rustc_driver::report_ice
6: std::panicking::rust_panic_with_hook
7: rust_begin_unwind
8: std::panicking::begin_panic_fmt
9: rustc_expand::expand::AstFragment::make_variants
10: <rustc_expand::mbe::transcribe::Frame as core::iter::traits::iterator::Iterator>::next
11: <rustc_expand::mbe::macro_rules::TokenSet as core::fmt::Debug>::fmt
12: _rust_maybe_catch_panic
13: rustc_expand::base::MacEager::ty
14: rustc_expand::base::MacEager::ty
15: proc_macro::bridge::scoped_cell::ScopedCell<proc_macro::bridge::client::BridgeStateL>::replace
at /rustc/8d69840ab92ea7f4d323420088dd8c9775f180cd\/src\libproc_macro\bridge\scoped_cell.rs:74
16: proc_macro::bridge::client::{{impl}}::with::{{closure}}
at /rustc/8d69840ab92ea7f4d323420088dd8c9775f180cd\/src\libproc_macro\bridge\client.rs:284
17: std::thread::local::LocalKey<proc_macro::bridge::scoped_cell::ScopedCell<proc_macro::bridge::client::BridgeStateL>>::try_with
at /rustc/8d69840ab92ea7f4d323420088dd8c9775f180cd\src\libstd\thread\local.rs:262
18: std::thread::local::LocalKey<proc_macro::bridge::scoped_cell::ScopedCell<proc_macro::bridge::client::BridgeStateL>>::with
at /rustc/8d69840ab92ea7f4d323420088dd8c9775f180cd\src\libstd\thread\local.rs:239
19: proc_macro::bridge::client::BridgeState::with
at /rustc/8d69840ab92ea7f4d323420088dd8c9775f180cd\/src\libproc_macro\bridge\client.rs:283
20: proc_macro::bridge::Bridge::with
at /rustc/8d69840ab92ea7f4d323420088dd8c9775f180cd\/src\libproc_macro\bridge\client.rs:314
21: proc_macro::bridge::client::Ident::new
at /rustc/8d69840ab92ea7f4d323420088dd8c9775f180cd\/src\libproc_macro\bridge\client.rs:230
22: proc_macro2::imp::Ident::new
at C:\Users\leesy\.cargo\registry\src\github.com-1ecc6299db9ec823\proc-macro2-1.0.12\src\wrapper.rs:631
23: proc_macro2::Ident::new
at C:\Users\leesy\.cargo\registry\src\github.com-1ecc6299db9ec823\proc-macro2-1.0.12\src\lib.rs:866
The docs don't make it clear that this setup of calling Ident::new with a r#-prefixed ident won't generate a valid Ident. Without the Ident::new_raw being stableized the only way I can see(from my very rudimentary understanding on how this magic works) is to call syn::parse_str twice:
It would be good to have some extra information in the documentation about how to handle
r#
-prefixed identifiers. For example:Some background about how I ended up here
I'm trying to generate some Rust structs from a file; and the names could be rust identifiers. In my code I used the
Ident::new
to convert the field name into a identifier:This generates a valid ident, but the macro that uses it won't compile when the
field_name
is a rust keyword(likelet
). My first attempt to fix this involved this:However using this code causes the compiler to crash during the build.
The docs don't make it clear that this setup of calling
Ident::new
with ar#
-prefixed ident won't generate a valid Ident. Without theIdent::new_raw
being stableized the only way I can see(from my very rudimentary understanding on how this magic works) is to callsyn::parse_str
twice:The text was updated successfully, but these errors were encountered: