Cannot find attribute getter in this scope with pyo3 and macro

Below code fails with error message "cannot find attribute getter in this scope".
Why is 'getter' not in scope?

use std::collections::HashMap;
use std::str;

use pyo3::prelude::*;

#[macro_export]
macro_rules! create_map_getter {
    ($(#[$m:meta])* $key:ident, $class:ident) => {
        $(#[$m])*
        fn $key(slf: PyRef<Self>) -> PyResult<HashMap<String, $class>> {
            Ok(slf
                .0
                .$key
                .iter()
                .map(|(k, v)| (str::from_utf8(k).unwrap().to_owned(), $class(v.clone())))
                .collect::<HashMap<String, $class>>())
        }
    };
}


#[derive(Debug, Clone)]
pub struct Value {
    pub foo: u32
}

#[derive(Debug, Clone)]
pub struct Data {
    pub foo: HashMap<Vec<u8>, Value>
}

#[pyclass]
#[derive(Debug, Clone)]
pub struct PyValue(Value);

#[pyclass]
#[derive(Debug, Clone)]
pub struct PyData(Data);

#[pymethods]
impl PyData {
    create_map_getter!(
        #[getter]
        foo,
        PyValue
    );
}

fn main() {
    println!("Hello, world!");
}

Macros are expanded outside-in, so what you are trying to achieve probably won't work.

Instead of expanding create_map_getter!() first and passing the fully formed getter() method to #[pymethods], the compiler will pass the literal tokens ["create_map_getter", "!", "(", ..., ")"] to #[pymethods], which probably just ignores them.

You can double-check this by pointing cargo expand to your module and seeing what the compiler gets after expanding all macros.

There are a handful of builtin macros which can expand their contents (e.g. concat!(env!("..."), "bar") will first expand the env!() call to get something like "foo", then concatenate things to generate "foobar"), but those are the exception to the rule.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.