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!
You might be okay with this:
macro_rules! span { ($line:expr, $column:expr) => { Span { line: $line, column: $column, file_path: None, } }; ($line:expr, $column:expr, $file_path:literal) => { Span { line: $line, column: $column, file_path: Some($file_path.to_string()), } }; ($line:expr, $column:expr, $file_path:expr) => { Span { line: $line, column: $column, file_path: $file_path, } }; }
However, sometimes I don’t want to pass in the file path directly but through a variable that is Option<String>.
Essentially I took this to mean
str
literals will be auto wrapped inSome
, but anything else is expected to beOption<String>
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_string
on it seems kinda hiding the allocation. I’d suggest let the user callto_string
on it themselves.(e) Changed it a bit to account for passing
None
as the third argument.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<string>
deleted by creator
* Two of your macro rules are not used 😉 (expand to see which ones).- This doesn’t support
Option<&str>
. If it did, we would lose literalNone
support 😉
deleted by creator
The macro rules are all used.
Oops. I was looking at it wrong.
I didn’t make
Option<&str>
an option because the struct is for typeOption<String>
.Re-read the end of OP’s requirements.
deleted by creator
Yes, but then the concrete type of
None
literals becomes unknown, which is what I was trying to point out.
- This doesn’t support
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<T>-for-Option<T>
This does not work, as rust cannot infer the type of
path
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
T
whereimpl From <Option<T>> for String
is implemented?
Then we would have a conflict.If you only need this for
&str
andString
, then you can add a wrapper typeOptionStringWrapper(Option<String>)
and implementFrom<T> for OptionStringWrapper
for all concrete type cases you want to support, and go from there.Right, there may be too many unknowns involved. 🤔
deleted by creator
Why not add a
new()
function that does the same? Macro seems unneccessary