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. 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!
16 Comments
Ogeon@programming.dev · 17 pts · 2y
Option<T>has aFrom<T>implementation that lets you writeOption::from($file_path).map(|path| path.to_string())to accept both cases in the same expression.https://doc.rust-lang.org/std/option/enum.Option.html#impl-From%3CT%3E-for-Option%3CT%3E
silva@sopuli.xyz · 2 pts · 2y
This does not work, as rust cannot infer the type of
pathBB_C@programming.dev · 2 pts · 2y
A generic impl is impossible.
Imagine you want to turn a
Into<String>toSome(val.into())andOption<Into<String>>toval.map(Into::into).Now, what if there is a type
Twhereimpl From <Option<T>> for Stringis implemented?Then we would have a conflict.
If you only need this for
&strandString, then you can add a wrapper typeOptionStringWrapper(Option<String>)and implementFrom<T> for OptionStringWrapperfor all concrete type cases you want to support, and go from there.Ogeon@programming.dev · 1 pts · 2y
Right, there may be too many unknowns involved. ๐ค
Ogeon@programming.dev · 1 pts · 2y
crispy_kilt@feddit.de · 15 pts · 2y
Why not add a
new()function that does the same? Macro seems unneccessaryKillTheMule@programming.dev · 6 pts · 2y
It's surprisingly simple: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=f176852c61dcf0c3382f0ac97c26de03 As a side node, asking for a value, and then immediately calling
to_stringon it seems kinda hiding the allocation. I'd suggest let the user callto_stringon it themselves.(e) Changed it a bit to account for passing
Noneas the third argument.Miaou@jlai.lu · 2 pts · 2y
I think the point is that the variable itself is an Option. Your example only works for literal Option (although the value inside the optional itself might not be a literal).
One option to OP's problem is to use an auxiliary trait implemented on both string and Option
v9CYKjLeia10dZpz88iU@programming.dev · 1 pts · 2y
This looks something like the following
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=aa68fe540dbd09821ecc482423227b46
BB_C@programming.dev · 1 pts · 2y
* Two of your macro rules are not used ๐ (expand to see which ones).Option<&str>. If it did, we would lose literalNonesupport ๐Similar impl, but using wrapper struct with
Fromimplsv9CYKjLeia10dZpz88iU@programming.dev · 1 pts · 2y
The 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.)
I didn't make Option<&str> an option because the struct is for type
Option<String>. It does support Option though.It looks like the following, and uses the last match case.
With macro expansion
There's not anything stopping it from supporting Option<&str> though. This would be the implementation
BB_C@programming.dev · 1 pts · 2y
Oops. I was looking at it wrong.
Re-read the end of OP's requirements.
v9CYKjLeia10dZpz88iU@programming.dev · 1 pts · 2y
I made an edit.
There's not anything stopping it from supporting Option<&str> though. This would be the implementation
It's also possible to just make it generic over Option types
BB_C@programming.dev · 1 pts · 2y
Yes, but then the concrete type of
Noneliterals becomes unknown, which is what I was trying to point out.tuna@discuss.tchncs.de · 1 pts · 2y
You might be okay with this:
Playground
Essentially I took this to mean
strliterals will be auto wrapped inSome, but anything else is expected to beOption<String>