Hey,
Is there any way to create a macro that allows a Some<T>
or T
as input?
It’s for creating a Span
struct that I’m using:
struct Span {
line: usize,
column: usize,
file_path: Option<String>,
}
…and I have the following macro:
macro_rules! span {
($line:expr, $column:expr) => {
Span {
line: $line,
column: $column
file_path: None,
}
};
($line:expr, $column:expr, $file_path: expr) => {
Span {
line: $line,
column: $column
file_path: Some($file_path.to_string()),
}
};
}
…which allows me to do this:
let foo = span!(1, 1);
let bar = span!(1, 1, "file.txt");
However, sometimes I don’t want to pass in the file path directly but through a variable that is Option<String>. To do this, I always have to match the variable:
let file_path = Some("file.txt");
let foo = match file_path {
Some(file_path) => span!(1, 1, file_path),
None => span!(1, 1),
}
Is there a way which allows me to directly use span!(1, 1, file_path)
where file_path
could be "file.txt"
, Some("file.txt")
or None
?
Thanks in advance!
* Two of your macro rules are not used 😉 (expand to see which ones).Option<&str>
. If it did, we would lose literalNone
support 😉Similar impl, but using wrapper struct with
From
implsThe macro rules are all used. (Macros are matched from top to bottom by the declared match types. The ident/expressions can’t match until after the more text based Option matching.)
let _foo = Span { line: 1, column: 1, file_path: None }; let _bar = Span { line: 1, column: 1, file_path: "file.txt".upgrade() }; let _baz = Span { line: 1, column: 1, file_path: Some("file.txt".to_string()) }; let _baz = Span { line: 1, column: 1, file_path: None }; let _baz = Span { line: 1, column: 1, file_path: borrowed.upgrade() }; let _baz = Span { line: 1, column: 1, file_path: owned.upgrade() };
I didn’t make Option<&str> an option because the struct is for type
Option<String>
. It does support Option<String> though.impl OptionUpgrade for Option<String> { fn upgrade(self) -> Option<String> { self } }
It looks like the following, and uses the last match case.
let opt: Option<String> = Some("text".into()); let opt = span!(1, 1, opt);
With macro expansion
let opt: Option<String> = Some("text".into()); let opt = Span { line: 1, column: 1, file_path: opt.upgrade() };
There’s not anything stopping it from supporting Option<&str> though. This would be the implementation
impl OptionUpgrade for Option<&str> { fn upgrade(self) -> Option<String> { self.map(|v| v.into()) } }
Oops. I was looking at it wrong.
Re-read the end of OP’s requirements.
I made an edit.
There’s not anything stopping it from supporting Option<&str> though. This would be the implementation
impl OptionUpgrade for Option<&str> { fn upgrade(self) -> Option<String> { self.map(|v| v.into()) } }
It’s also possible to just make it generic over Option types
impl<A: ToString> OptionUpgrade for Option<A> { fn upgrade(self) -> Option<String> { self.map(|v| v.to_string()) } }
Yes, but then the concrete type of
None
literals becomes unknown, which is what I was trying to point out.deleted by creator
I had commented something similar to stating it was possible to pass the inference problem to the struct if the goal was to support more types, but I removed it because I didn’t think my example was easy to understand when reading it later. I made a better example though
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=8651fa6121840658b4b6249399f693c7