error[E0616]: field `inner` of struct `Foo` is private
--> src/lib.rs:24:9
|
24 | arg.inner
| ^^^^^ private field
But this prevents you from using the macro in certain environments:
// should work but doesn't
fn main() {
foo! { 10 }
}
error[E0425]: cannot find value `ARG` in module `super`
--> src/main.rs:14:41
|
14 | self.inner + super::ARG
| ^^^ not found in `super`
...
22 | foo! { 10 }
| ----------- in this macro invocation
This is a problem because documentation without a main function is implicitly wrapped in a main function, causing the above situation. (Note that this is issue #130274.)
How can I have macros emit types with private fields without making the above case break?
error[E0425]: cannot find value `MY_CONST` in this scope
--> src/lib.rs:27:8
|
27 | foo! { MY_CONST }
| ^^^^^^^^ not found in this scope
|
= help: consider importing this constant:
crate::MY_CONST
The closest I think you can get is to hide the field in a boxed trait object. This has the downside of limiting you to an object-safe impl and a slight perf impact, but I believe Bar.0 should be completely inaccessible outside new().
(And just to be sure, Foo is intended to be mutable, just only by macro-generated methods?)
A {problem with, feature of} macros is that aside from compile time calculations and syntactic abstraction, what they can do must be manually possible as well. They are (conceptually) heavily template-based, after all.
One thing you might be able to do depending on your use case is turn it into an attribute macro, apply it to a unit struct, and have the attribute macro generate the field and methods:
#[foo(42)]
struct Foo;
Technically that won't prevent access if you don't also generate a module, but it will be hidden better (and therefore more difficult to muck about with it as a consumer) than it would be with a macro_rules! macro, and it would of course be undocumented (or you can explicitly document it as "manual manipulation unsupported" if you prefer), and therefore arguably not part of the public API.
It's not perfect but it would work.
A possible alternative or even augmentation could be to also write a trait with the necessary functionality, generate an impl Trait for the generated type, and then turn each new value of that type into a trait object (assuming the functionality allows for that) before handing it over to the consumer, thereby making direct field manipulation impossible in the first place.
To have more fine-grained control (eg. if you want a setter but not a getter) you can implement a trait like this:
trait Get<T> {
fn get(&self) -> T;
}
impl<T> Get<T> for PrivateFieldGet<T>
where
T: Clone,
{
fn get(&self) -> T {
self.0.clone()
}
}
In the generation of the struct, we wrap private fields within this new type.
macro_rules! generate_struct {
(...) => {
struct {
// Allows to only set this variable once but never read it.
inner_private: mycrate::PrivateField(u32),
// We set the value initially and can use a getter function to access it
inner_private_get: mycrate::PrivateFieldGet(u32),
// Classic struct field which is public for the user
inner_public: u32,
...
}
}
}
The main problem here is of course the syntax. Having to access a field with foo.inner.get() is unwieldy compared to the standard foo.inner but Rust can not distinguish between "get-only" or "get-and-set" field types (not even inside your own crate). Thus we need to rely on traits or other functions.