I have an
enum EntryPoints {
A(),
B(),
}
Is it possible to create a macro to adds an enum case, like this?
use some_lib::make_callback;
enum EntryPoints {
A(),
B(),
make_callback!(),
}
which expands to smething like
enum EntryPoints {
A(),
B(),
SomeLibCallback(data: u64),
}
All my attempts to create a very simple example lead to syntax errors and I don't find anything on the web.
You can't call macros within the body of an enum declaration, but you can pass the enum declaration into the macro instead:
macro_rules! make_callback {
{ enum $name:ident { $($inner:tt)* } } => {
enum $name {
SomeLibCallback(u64),
$($inner)*
}
}
}
make_callback! { enum EntryPoints {
A(),
B(),
} }
// expansion
enum EntryPoints {
SomeLibCallback(u64),
A(),
B(),
}
{ $($inner:tt)* } essentially just says to match any arbitrary sequence of tokens within the set of curly braces, and $($inner)* then expands that sequence back into the new enum body.
Related
How can I expose a struct generated from the quote macro in my derive macro without having to introduce a struct name out of the blue in my usage file (due to macro expansion)?
To illustrate the point, currently, my code looks something like this:
// "/my_derive/lib.rs"
// inside a derive macro function
let tokens = quote! {
struct MyDeriveMacroInternalStruct {
variant: #ident_name,
// other stuff ...
}
impl #ident_name {
pub fn something() -> Vec<MyDeriveMacroInternalStruct> {
vec![MyDeriveMacroInternalStruct { variant: #ident_name::#variant_name, /*...*/ }, /*...*/]
}
}
};
tokens.into()
The usage of my code would look something like this:
use my_derive::MyDerive;
#[derive(MyDerive)]
enum Something {
A,
B,
C,
}
fn process_data() -> Vec<MyDeriveMacroInternalStruct> { // having to write that struct name that came out of nowhere bothers me
Something::something()
}
fn main() {
let result = process_data();
// do stuff...
}
This is a condensed version of my actual code (process_data is in another file). To reiterate my question in light of the example, how can I access the struct without having it randomly appear out of nowhere (due to macro expansion)? To me the code unchanged is hard to understand, read, and change.
I would like to be able to do something like this:
use my_derive::{MyDerive, MyDeriveStruct};
#[derive(MyDerive)]
enum Something {
A,
B,
C,
}
fn process_data() -> Vec<MyDeriveStruct> { // importing the struct instead of magically appearing
Something::something()
}
fn main() {
let result = process_data();
// do stuff...
}
Obviously the idea seems quite stupid, but there has to be a way around it (an arbitrary struct definition). If what I imagined isn't possible, is there some way to be more clear about where the random struct came from?
Actually I thought of something better. Your derive should probably be associated with a trait of the same name.
Add an associated type to your trait:
trait MyDerive {
type Output;
...
}
Then set the associated type when you impl the trait:
struct MyDeriveMacroInternalStruct {
variant: #ident_name,
// other stuff ...
}
impl MyDerive for #ident_name {
type Output = MyDeriveMacroInternalStruct;
pub fn something() -> Vec<MyDeriveMacroInternalStruct> {
vec![MyDeriveMacroInternalStruct { variant: #ident_name::#variant_name, /*...*/ }, /*...*/]
}
}
Then you can refer to that associated type in return position or wherever:
use my_derive::MyDerive;
#[derive(MyDerive)]
enum Something {
A,
B,
C,
}
fn process_data() -> Vec<<Something as MyDerive>::Output> {
Something::something()
}
fn main() {
let result = process_data();
// do stuff...
}
Note: the convention is for #[derive(Trait)] to correspond to an impl for the given Trait, but your proc macro crate can't export a trait directly for importing in your library code.
So generally the solution is to have two crates:
my-trait is the "library" crate which contains the MyTrait trait definition
my-trait-derive is the proc-macro crate which contains the derive macro code
my-trait has my-trait-derive as a direct dependency, and re-exports the proc macro from it:
// my-trait lib.rs
pub use my_trait_derive::MyTrait;
// macro and trait names can overlap as they're
// treated as different item kinds
pub trait MyTrait {
type Output;
fn something();
}
see how clap does it here (they also re-export the whole clap_derive)
Then a user can use your proc macro + trait like this:
use my_trait::MyTrait;
#[derive(MyTrait)]
enum Something {}
fn process_data() -> Vec<<Something as MyTrait>::Output> {
Something::something()
}
Older Answer
What I would do is create a trait MyDeriveOutput or something with whatever stuff you want exposed from MyDeriveMacroInternalStruct:
trait MyDeriveOutput {
fn variant() ...
}
And then generate an impl for each internal struct you create:
struct MyDeriveMacroInternalStruct {
variant: #ident_name,
// other stuff ...
}
impl MyDeriveOutput for MyDeriveMacroInternalStruct {
// whatever
}
Then you can expose the trait and require it to be imported and used with impl Trait in return position:
use my_derive::{MyDerive, MyDeriveOutput};
#[derive(MyDerive)]
enum Something {
A,
B,
C,
}
fn process_data() -> Vec<impl MyDeriveOutput> {
Something::something()
}
fn main() {
let result = process_data();
// do stuff...
}
I've just learned that an Enum can be initialized with a custom data type.
Wouldn't that make Enums a subset of Structs? An Enum could be a Struct with a single unnamed variable which is initialized once and cannot be changed (like final variables in Java). Also, no methods can be implemented to enums.
Like this:
enum E {
ONE(String)
}
struct S {
one: String
}
Thus, in memory, both a single variable struct and enum would look the same.
Is this true or am I missing something?
It's actually the opposite: structs are subsets of enums. Any struct can be represented as one-variant enum:
struct Record { ... }
struct TupleLike(...);
enum Record { Variant { ... } }
enum TupleLike { Variant(...) }
This enum will even have the same in-memory representation! (though it isn't guaranteed).
On the other hand, enums with multiple variants cannot be described precisely as structs. They are usually implemented as tagged unions, for example:
enum E {
S(String),
I(i32),
}
E::I(123);
type E_Discriminant = u8;
const E_S: E_Discriminant = 0;
const E_I: E_Discriminant = 1;
union E_Payload {
s: String,
i: i32,
}
struct E {
discriminant: E_Discriminant,
payload: E_Payload,
}
E { discriminant: E_I, payload: E_Payload { i: 123 } };
But even doing that manually will not provide you the whole language experience of using enums: you will unable to use pattern matching, accessing variants will be unsafe (and dangerous), etc..
However, when only one variant is needed, structs are more comfortable to use, and that's why they're there.
I have made a minimal example. In lib.rs:
mod sealed {
pub enum Choice {
A,
B,
}
}
pub fn print_choice(choice: sealed::Choice) {
match choice {
sealed::Choice::A => println!("Choice A"),
sealed::Choice::B => println!("Choice B"),
}
}
I think: The enum Choice is public. However, it's in a private mod, and cannot be reached from outside of the crate. Therefore the function print_choice is not callable at all.
What is wrong with my thinking?
What is wrong with my thinking?
You could have something like
pub use sealed::Choice;
at the toplevel. That is a common way to split up the implementation while providing a simple single-module interface to external users.
Or even just an other function returning an instance of Choice. Since it's pub, it's not considered a private type.
If you change pub enum to pub(crate) enum (meaning you state that the enum can not be made visible outside the crate) then the compilation will fail.
An important thing to understand is that Choice is not private. It is inside a private module, and thus unnamable, but it is public.
The only thing the module's privacy affects is that you cannot access the enum via this path. You can do any other thing with it, e.g. accessing it via other path it is reexported into:
mod sealed {
pub enum Choice {
A,
B,
}
}
pub use sealed::Choice;
// In other module
crate::Choice::A;
Or manipulate it with generics and traits, for example:
mod sealed {
pub enum Choice {
A,
B,
}
impl Default for Choice {
fn default() -> Self { Self::A }
}
}
pub fn print_choice(choice: sealed::Choice) { ... }
// In other module
crate::print_choice(Default::default());
mod sealed {
#[derive(Debug)]
pub enum Choice {
A,
B,
}
}
pub fn print_choice(choice: sealed::Choice) { crate::print(choice) }
// In other module
pub fn print<T: Debug>(v: T) { ... }
This private type seems to leak, but you don't have full control over it.
You cannot build such a private Choice, but another public function can provide you with it.
mod outer {
mod sealed {
pub enum Choice {
A,
B,
}
}
pub fn print_choice(choice: sealed::Choice) {
match choice {
sealed::Choice::A => println!("Choice A"),
sealed::Choice::B => println!("Choice B"),
}
}
pub fn make_choice(n: u32) -> sealed::Choice {
if n % 2 == 0 {
sealed::Choice::A
} else {
sealed::Choice::B
}
}
}
fn main() {
// let ch = outer::sealed::Choice::A; // error: module `sealed` is private
let ch = outer::make_choice(2);
outer::print_choice(ch);
}
I learned from this answer that I can generate docs from macros:
macro_rules! gen_fn {
(
$(#[$attr: meta])*
$name:ident
) => {
$(#[$attr])*
pub fn $name(self) {}
}
}
struct Baz {}
impl Baz {
gen_fn! {
/// This is a doc
/// with a very long text.
my_func
}
}
fn main() {
let o = Baz{};
o.my_func(); // generated method, with correct doc
}
However, if I pass more than one doc line to gen_fn, it interprets each line as a new #[doc], thus transforming each line in to a new paragraph:
struct Baz {}
impl Baz {
gen_fn! {
/// This is a doc
/// with a very long text.
=> my_func
}
}
Instead of "This is a doc with a very long text"*, it becomes two paragraphs:
How can I rewrite gen_fn so that it doesn't transform each new line into a paragraph?
Addendum:
I hacked out a way by directly inserting strings into a #[doc], but it looks ugly, so I wonder if is there a more elegant way:
macro_rules! gen_fn {
($name: ident, $comm: expr) => {
#[doc=$comm]
pub fn $name(self) {}
}
}
struct Baz {}
impl Baz {
gen_fn!(my_func,
"This is a doc
with a very long text.");
}
Update:
This was indeed a bug in rust-analyzer, and it was fixed here:
https://github.com/rust-analyzer/rust-analyzer/pull/6743
Looks fine to me when documented via cargo doc with Rust 1.48.0:
<div class="docblock"><p>This is a doc
with a very long text.</p>
</div>
The issue appears to be with how your editor is showing the documentation.
I work with a bunch of structs / enums included in each other. I need to get ty.node<TyKind::Path>.1.segments.last().identifiers and ty.node<TyKind::Path>.1.segments.last().parameters<AngleBracketed::AngleBracketed>.types.
Is there a simpler way to get these two values then my implementation of f? My ideal syntax would be:
ty.node<TyKind::Path>?.1.segments.last().identifiers
// and
ty.node<TyKind::Path>?.1.segments.last().parameters<AngleBracketed::AngleBracketed>?.types
It that's impossible, maybe there is a way to reduce the number of if let? I want to solve only this particular case, so simplification should be possible compared to f. If an analog of Option::map / Option::unwrap_or_else were introduced, then the sum of its code + the code in f should be less then my original f.
#[derive(Clone)]
struct Ty {
node: TyKind,
}
#[derive(Clone)]
enum TyKind {
Path(Option<i32>, Path),
}
#[derive(Clone)]
struct Path {
segments: Vec<PathSegment>,
}
#[derive(Clone)]
struct PathSegment {
identifier: String,
parameters: Option<Box<PathParameters>>,
}
#[derive(Clone)]
enum PathParameters {
AngleBracketed(AngleBracketedParameterData),
}
#[derive(Clone)]
struct AngleBracketedParameterData {
types: Vec<Box<Ty>>,
}
/// If Tylnode == Path -> return last path segment + types
fn f(ty: &Ty) -> Option<(String, Vec<Box<Ty>>)> {
match ty.node {
TyKind::Path(_, ref path) => if let Some(seg) = path.segments.iter().last() {
let ident = seg.identifier.clone();
println!("next_ty: seg.id {:?}", seg.identifier);
match seg.parameters.as_ref() {
Some(params) => match **params {
PathParameters::AngleBracketed(ref params) => {
Some((ident, params.types.clone()))
}
_ => Some((ident, vec![])),
},
None => Some((ident, vec![])),
}
} else {
None
},
_ => None,
}
}
To simplify the question, I have removed unrelated enum variants and struct fields.
No.
The closest you can get, using nightly features and helper code, is probably this
fn f(ty: &Ty) -> MyOption<(String, Vec<Box<Ty>>)> {
let last = ty.node.path()?.segments.my_last()?;
Just((
last.identifier.clone(),
last.ab_parameters()
.map(|v| v.types.clone())
.unwrap_or_else(|| vec![]),
))
}
Playground
I guess what you want is called Lenses. Not sure about Rust, but here is about Haskell https://en.m.wikibooks.org/wiki/Haskell/Lenses_and_functional_references
It might be possible to implement that in Rust, if somebody haven't done yet.