Force code to be ran inside a macro at compile time

Is there a way to fail to compile code because it is not wrapped in a macro? For example:

struct MyStruct(i32);

fn main() {
    allow! {
      // this would be fine because it is inside my special `allow` macro
      let my_struct = MyStruct(100);
    }
    // but this would fail at compile time because it is not
    let my_struct = MyStruct(100);
}

Any tips?

I don’t think that there’s any way to do this currently, and there’s no plausible reason why you’d do this; could you please expand why you may want to do this?

PS you could always make the structure name a macro…

PS you could always make the structure name a macro…

I would be doing multiple operations that all require the same constraint inside the macro, but just simplified it for the example.

there’s no plausible reason why you’d do this

I’m not so sure. Consider going across a ffi boundry that requires to you initialize something before and clean it up after. Right now, it would look something like this:

struct Guard;
impl Guard { fn new() -> Self { Guard } } // do some "before stuff" here 

struct MyStruct<'a>(&'a Guard, i32);

fn main() {
    let guard = Guard::new();
    // the reference to guard that MyStruct owns ensures the "before stuff" was done
    let my_struct = MyStruct(&guard, 100);
    // impl `std::ops::drop` for `Guard` for some "after stuff"
}

But as you can see, if I could make some macro like I mentioned, it would clean this up a lot and be pretty cool :D.

1 Like

Ah! Then you could use something like so (Even though it isn’t as strict as an error or a warning, at this point it’s just a convention):

fn ffi_boundary<T>(x: impl FnOnce() -> T) -> T {
    //Prepare some stuff beforehand
    let val = x();
    //Do some stuff after
    val //Optional
}

So that it can be called like so:

ffi_boundary(|| {
    let _ = MyStruct(100);
});
1 Like

Yeah that is a cool idea, but it would still require me to mark my public interface to be unsafe because it wouldn’t stop users to create a MyStruct(100); outside of the fn ffi_boundary(). I like the idea though.

You could pass in a factory object.
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=d2df210e4d546ff95b47d7c8c2f9f97e

I made it hold a reference to the guard so trickery can’t be used to pass it outside the closure (possible in safe-code with catch_unwind otherwise) and make takes the Factory by value so it can only be called once.

What you could do, then, is keep the guard object, but use the macro to inject it.

allow!{
    let my_struct = MyStruct(100);
}
// The code above desugars into:
let guard = Guard::new();
let my_struct = MyStruct(&guard, 100);

It would probably be a procedural macro based on syn, but anyway, that’s how I’d do it.