Is there a way in Rust to get the "calling" function name or any other contextual information inside a macro?
Example:
#[macro_export]
macro_rules! somemacro {
( $x:expr ) => {
{
// access function name (etc..) that called this macro
}
};
}
This can be done using a procedural macro:
extern crate proc_macro;
use proc_macro::TokenStream;
#[proc_macro_attribute]
pub fn with_name(_: TokenStream, item: TokenStream) -> TokenStream {
let mut input = syn::parse_macro_input!(item as syn::ItemFn);
let fn_name = input.ident.to_string();
let const_decl = quote::quote! {
const THIS_FN: &str = #fn_name;
};
input.block.stmts.insert(0, syn::parse(const_decl.into()).unwrap());
let output = quote::quote! {
#input
};
output.into()
}
Cargo.toml:
[package]
name = "with_name"
version = "0.1.0"
edition = "2018"
[lib]
proc-macro = true
[dependencies]
quote = "0.6.12"
syn = { version = "0.15.37", features = ["full"] }
Which can be used as:
#[with_name::with_name]
fn foo() {
println!("Name: {}", THIS_FN);
}
fn main() {
foo();
}
Also note that if you only care about the module, there is a built-in macro for that:
mod test {
pub fn foo() {
println!("Module: {}", module_path!());
}
}
fn main() {
test::foo();
}
(link to playground)
Related
I have two versions of a structure, one is enabled when a feature is enabled, the other when its not enabled, as shown below. The thing is that one contains unsafe methods, the other does not. So when these functions are called in other parts of the library, I have surrounded them with an unsafe { } block. When my library is compiled without the feature enabled, I get the following warning:
unnecessary unsafe block
How do I get around / disable this warning when its not applicable, ie. when the feature is not enabled.
#[cfg(feature = "my-feature")]
pub struct MyStruct {
// some fields
}
#[cfg(not(feature = "my-feature"))]
pub struct MyStruct {
// some different fields
}
#[cfg(feature = "my-feature")]
impl MyStruct {
pub unsafe fn my_func() {}
}
#[cfg(not(feature = "my-feature"))]
impl MyStruct {
pub fn my_func() {}
}
fn main() {
// I want to get rid of this unsafe when its not needed.
unsafe { MyStruct::my_func() };
}
If these cases are only inside your library, so you can afford little incovenience, you can use a macro:
macro_rules! call {
// For static methods.
{
unsafe {
$($method:ident)::+ ( $($arg:expr),* $(,)? )
}
} => {{
#[cfg(feature = "my-feature")]
let v = unsafe { $($method)::+ ( $($arg),* ) };
#[cfg(not(feature = "my-feature"))]
let v = $($method)::+ ( $($arg),* );
v
}};
// For instance methods.
{
unsafe {
$object:ident . $method:ident ( $($arg:expr),* $(,)? )
}
} => {{
#[cfg(feature = "my-feature")]
let v = unsafe { $object . $method ( $($arg),* ) };
#[cfg(not(feature = "my-feature"))]
let v = $object . $method ( $($arg),* );
v
}};
}
call!(unsafe { MyStruct::my_func() });
call!(unsafe { v.my_method() });
This macro does not allow generics or complex expression as the receiver. Extending it to support generics should be fairly easy; supporting complex expressions will probably require a proc macro.
There is a better way to write the macro: instead of requiring unsafe inside it, and wrapping the call in cfg, just do something unsafe inside it, thus always requiring unsafe outside of it. This also solves the problem with generics and complex receivers:
pub(crate) unsafe fn always_unsafe() {}
macro_rules! call {
($($call:tt)+) => {{
$crate::always_unsafe();
$($call)*
}};
}
unsafe {
call!(MyStruct::my_func());
}
unsafe {
call!(v.my_method());
}
I'm trying to figure out how to access the EnumValueOption len associated with each member of Foo:
// proto/demo.proto:
syntax = "proto3";
import "google/protobuf/descriptor.proto";
package demo;
extend google.protobuf.EnumValueOptions {
optional uint32 len = 50000;
}
enum Foo {
None = 0 [(len) = 10];
One = 1 [(len) = 20];
Two = 2 [(len) = 30];
}
I think I should be able to do this through prost_types::FileDescriptorSet by collecting the reflection information at build time:
// build.rs:
use std::path::PathBuf;
fn main() {
let out_dir =
PathBuf::from(std::env::var("OUT_DIR").expect("OUT_DIR environment variable not set."));
prost_build::Config::new()
.file_descriptor_set_path(out_dir.join("file_descriptor_set.bin"))
.compile_protos(&["proto/demo.proto"], &["proto"])
.unwrap_or_else(|e| panic!("Failed to compile protos {:?}", e));
}
But I can't figure out how to actually retrieve the len field:
// src/lib.rs
use prost::Message;
use prost_types::FileDescriptorSet;
use prost_types::EnumValueOptions;
include!(concat!(env!("OUT_DIR"), concat!("/demo.rs")));
pub fn parse_file_descriptor_set() -> FileDescriptorSet {
let file_descriptor_set_bytes =
include_bytes!(concat!(env!("OUT_DIR"), "/file_descriptor_set.bin"));
prost_types::FileDescriptorSet::decode(&file_descriptor_set_bytes[..]).unwrap()
}
#[cfg(test)]
mod tests {
use super::*;
fn get_len(foo: Foo) -> u32 {
let set = parse_file_descriptor_set();
for f in &set.file {
for ext in &f.extension {
dbg!(ext);
}
for e in &f.enum_type {
dbg!(e);
for v in &e.value {
dbg!(v);
}
}
}
todo!()
}
#[test]
fn test_get_len() {
assert_eq!(get_len(Foo::One), 20);
}
}
Am I on the right track? Is this something that's even supported? I'm using prost, prost-types, and prost-build version 0.9.
How can I create a new vector with the iterator Rust macro syntax?
I am trying this:
unsafe {
MUT_STATIC_VAR = vec![
#(#my_outher_vector_data)*,
];
}
Full explanation: I am trying to reasign data that I write in one mut static var of type: Vec when the macro it's spanded at compile time. When I try to retrieve the data at runtime, the global it's empty, so I am rewiring the data that I want in main().
Recap. I am just want to assign the content of one vector to another, but neither array or Vec<T> implements ToTokens.
Compiler error:
`main` function not found in crate `my_crate`
Thanks
To initialize the content, Iterators are able to use macro #(#...)*, syntax.
let other_as_iter = my_outher_vector_data.iter();
quote {
unsafe {
MUT_STATIC_VAR = vec![
#(#other_as_iter)*,
];
}
}
I think lazy_static should do the job:
#[macro_use]
extern crate lazy_static;
use std::sync::Mutex;
lazy_static! {
static ref MUT_STATIC_VAR: Mutex<Vec<String>> = Mutex::new(vec![]);
}
fn other_vec(v: Vec<String>) {
let mut r = MUT_STATIC_VAR.lock().unwrap();
r.extend_from_slice(v.as_slice());
}
fn main() {
other_vec(vec!["dog".to_string(), "cat".to_string(), "mouse".to_string()]);
}
…or draining the other vec after initializing MUT_STATIC_VAR:
fn other_vec(v: &mut Vec<String>) {
let mut r = MUT_STATIC_VAR.lock().unwrap();
r.extend_from_slice(v.drain(..).as_slice());
}
fn main() {
other_vec(&mut vec!["dog".to_string(), "cat".to_string(), "mouse".to_string()]);
}
…or my_other_vector_data wrapped in other_vec! macro:
Playground
macro_rules! other_vec {
() => {
vec!["dog", "cat", "mouse"] // my_other_vector_data here
};
}
I've found the following solution to create a macro that defines a function which returns true if an enum matches a variant:
macro_rules! is_variant {
($name: ident, $enum_type: ty, $enum_pattern: pat) => {
fn $name(value: &$enum_type) -> bool {
matches!(value, $enum_pattern)
}
}
}
Usage:
enum TestEnum {
A,
B(),
C(i32, i32),
}
is_variant!(is_a, TestEnum, TestEnum::A);
is_variant!(is_b, TestEnum, TestEnum::B());
is_variant!(is_c, TestEnum, TestEnum::C(_, _));
assert_eq!(is_a(&TestEnum::A), true);
assert_eq!(is_a(&TestEnum::B()), false);
assert_eq!(is_a(&TestEnum::C(1, 1)), false);
Is there a way to define this macro so that
providing placeholders for the variant data can be avoided?
In other words, change the macro to be able to use it like so:
is_variant!(is_a, TestEnum, TestEnum::A);
is_variant!(is_a, TestEnum, TestEnum::B);
is_variant!(is_a, TestEnum, TestEnum::C);
Using std::mem::discriminant, as described in Compare enums only by variant, not value, doesn't help since it can only be used to compare two enum instances. In this case there is only one single object and the variant identifier.
It also mentions matching on TestEnum::A(..) but that doesn't work if the variant has no data.
You can do that using proc macros. There is a chapter in rust book that may help.
Then you can use it like:
use is_variant_derive::IsVariant;
#[derive(IsVariant)]
enum TestEnum {
A,
B(),
C(i32, i32),
D { _name: String, _age: i32 },
}
fn main() {
let x = TestEnum::C(1, 2);
assert!(x.is_c());
let x = TestEnum::A;
assert!(x.is_a());
let x = TestEnum::B();
assert!(x.is_b());
let x = TestEnum::D {_name: "Jane Doe".into(), _age: 30 };
assert!(x.is_d());
}
For the above effect, proc macro crate will look like:
is_variant_derive/src/lib.rs:
extern crate proc_macro;
use proc_macro::TokenStream;
use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::{format_ident, quote, quote_spanned};
use syn::spanned::Spanned;
use syn::{parse_macro_input, Data, DeriveInput, Error, Fields};
// https://crates.io/crates/convert_case
use convert_case::{Case, Casing};
macro_rules! derive_error {
($string: tt) => {
Error::new(Span::call_site(), $string)
.to_compile_error()
.into();
};
}
#[proc_macro_derive(IsVariant)]
pub fn derive_is_variant(input: TokenStream) -> TokenStream {
// See https://doc.servo.org/syn/derive/struct.DeriveInput.html
let input: DeriveInput = parse_macro_input!(input as DeriveInput);
// get enum name
let ref name = input.ident;
let ref data = input.data;
let mut variant_checker_functions;
// data is of type syn::Data
// See https://doc.servo.org/syn/enum.Data.html
match data {
// Only if data is an enum, we do parsing
Data::Enum(data_enum) => {
// data_enum is of type syn::DataEnum
// https://doc.servo.org/syn/struct.DataEnum.html
variant_checker_functions = TokenStream2::new();
// Iterate over enum variants
// `variants` if of type `Punctuated` which implements IntoIterator
//
// https://doc.servo.org/syn/punctuated/struct.Punctuated.html
// https://doc.servo.org/syn/struct.Variant.html
for variant in &data_enum.variants {
// Variant's name
let ref variant_name = variant.ident;
// Variant can have unnamed fields like `Variant(i32, i64)`
// Variant can have named fields like `Variant {x: i32, y: i32}`
// Variant can be named Unit like `Variant`
let fields_in_variant = match &variant.fields {
Fields::Unnamed(_) => quote_spanned! {variant.span()=> (..) },
Fields::Unit => quote_spanned! { variant.span()=> },
Fields::Named(_) => quote_spanned! {variant.span()=> {..} },
};
// construct an identifier named is_<variant_name> for function name
// We convert it to snake case using `to_case(Case::Snake)`
// For example, if variant is `HelloWorld`, it will generate `is_hello_world`
let mut is_variant_func_name =
format_ident!("is_{}", variant_name.to_string().to_case(Case::Snake));
is_variant_func_name.set_span(variant_name.span());
// Here we construct the function for the current variant
variant_checker_functions.extend(quote_spanned! {variant.span()=>
fn #is_variant_func_name(&self) -> bool {
match self {
#name::#variant_name #fields_in_variant => true,
_ => false,
}
}
});
// Above we are making a TokenStream using extend()
// This is because TokenStream is an Iterator,
// so we can keep extending it.
//
// proc_macro2::TokenStream:- https://docs.rs/proc-macro2/1.0.24/proc_macro2/struct.TokenStream.html
// Read about
// quote:- https://docs.rs/quote/1.0.7/quote/
// quote_spanned:- https://docs.rs/quote/1.0.7/quote/macro.quote_spanned.html
// spans:- https://docs.rs/syn/1.0.54/syn/spanned/index.html
}
}
_ => return derive_error!("IsVariant is only implemented for enums"),
};
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let expanded = quote! {
impl #impl_generics #name #ty_generics #where_clause {
// variant_checker_functions gets replaced by all the functions
// that were constructed above
#variant_checker_functions
}
};
TokenStream::from(expanded)
}
Cargo.toml for the library named is_variant_derive:
[lib]
proc-macro = true
[dependencies]
syn = "1.0"
quote = "1.0"
proc-macro2 = "1.0"
convert_case = "0.4.0"
Cargo.toml for the binary:
[dependencies]
is_variant_derive = { path = "../is_variant_derive" }
Then have both crates in the same directory (workspace) and then have this Cargo.toml:
[workspace]
members = [
"bin",
"is_variant_derive",
]
Playground
Also note that proc-macro needs to exist in its own separate crate.
Or you can directly use is_variant crate.
I want to implement a static cache in rust.
I tried chashmap
extern crate chashmap;
extern crate lazy_static;
use chashmap::{CHashMap, ReadGuard};
use lazy_static::lazy_static;
use std::collections::HashMap;
struct Data {}
#[derive(Default)]
struct Merged {
types: HashMap<String, Data>,
}
fn merge(ls: &[String]) -> ReadGuard<'static, Vec<String>, Merged> {
lazy_static! {
static ref CACHE: CHashMap<Vec<String>, Merged> = Default::default();
}
let libs = ls.to_vec();
let merged = Merged::default();
CACHE.insert(libs, merged);
return CACHE.get(ls).unwrap();
}
fn get<'a>(ls: &[String], name: &str) -> Option<&'a Data> {
let lib = merge(ls);
if let Some(ty) = lib.types.get(name) {
return Some(&*ty);
}
None
}
fn main() {}
[package]
name = "stackoverflow-56728860"
version = "0.1.0"
authors = ["강동윤 <kdy1#outlook.kr>"]
edition = "2018"
[dependencies]
lazy_static = "1"
chashmap = "2"
github repo
But I want to return static reference to data from the function. The returned data solely depends on ls. That is, if input (ls) is same, the result would be same.
Also, leaking data is ok if read lock is released.
I somehow solved it with leaking.
fn merge(ls: &[Lib]) -> &'static Merged {
lazy_static! {
static ref CACHE: CHashMap<Vec<Lib>, &'static Merged> = Default::default();
}
// ...
CACHE.insert(libs, Box::leak(merged));
return &*CACHE.get(ls).unwrap();
}