-
Notifications
You must be signed in to change notification settings - Fork 9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Arg borrowing from Parser is inconvenient #7
Comments
Just brainstorming, could you turn |
One downside would be that you can't take ownership of the That could mean a lot of unnecessary copies. But arguments are already arguably-unnecessarily copied at least once by It could also remove access to A similar change could be to do that but make If this is taken far enough then perhaps all error messages could take full advantage of what I tried this at some point and ran into borrow checker issues in user code. I reproduced it (or something like it) by giving struct Parser;
struct Arg<'a>(&'a mut Parser);
struct ArgRef<'a>(&'a str);
impl Drop for ArgRef<'_> {
fn drop(&mut self) {}
}
impl Parser {
fn next(&mut self) -> Arg {
Arg(self)
}
}
impl Arg<'_> {
fn getref(&self) -> ArgRef {
ArgRef("foo")
}
fn value(&mut self) -> String {
"bar".to_owned()
}
}
fn main() {
let mut parser = Parser;
let mut arg = parser.next();
match arg.getref() {
_ => println!("{}", arg.value()),
}
} This code compiles only if you remove the I don't know if this would come up, and maybe jiggling who owns what could fix it, but it's something to watch out for. I think a lot of it depends on how commonly the lifetime is an issue. If 95% of code has no problems with the current system then it's ok if the solution for the other 5% is ugly, as long as it exists. |
I forgot about this issue, ran into this exact problem, and came up with my own work-around that looks pretty similar to what you have in the OP, but isn't its own function: // We do this little dance to disentangle the lifetime of 'p' from the
// lifetime on 'arg'. The cost is that we have to clone all long flag
// names to give it a place to live that isn't tied to 'p'. Annoying,
// but not the end of the world.
let long_flag: Option<String> = match arg {
Arg::Long(name) => Some(name.to_string()),
_ => None,
};
let mut arg = match long_flag {
Some(ref flag) => Arg::Long(flag),
None => match arg {
Arg::Short(c) => Arg::Short(c),
Arg::Long(_) => unreachable!(),
Arg::Value(value) => Arg::Value(value),
},
}; And indeed, I ran into exactly this issue where I have a bunch of different sub-command where I try to compose a bunch of different flags. ( |
Here's another solution: enum OwnedArg {
Short(char),
Long(String),
Value(OsString),
}
impl From<Arg<'_>> for OwnedArg {
fn from(arg: Arg<'_>) -> OwnedArg {
match arg {
Arg::Short(opt) => OwnedArg::Short(opt),
Arg::Long(opt) => OwnedArg::Long(opt.to_owned()),
Arg::Value(val) => OwnedArg::Value(val),
}
}
} Matching becomes problematic. You can use
Maybe this: impl OwnedArg {
fn is_short(&self, option: char) -> bool {
match self {
OwnedArg::Short(opt) => *opt == option,
_ => false,
}
}
fn is_long(&self, option: &str) -> bool {
match self {
OwnedArg::Long(opt) => opt == option,
_ => false,
}
}
}
if arg.is_short('v') || arg.is_long("verbose") {
// ...
} But that makes it tedious to adapt existing code that operates on an I notice there's been movement on deref patterns. This now compiles on nightly! #![feature(string_deref_patterns)]
match arg {
OwnedArg::Short('v') | OwnedArg::Long("verbose") => (),
_ => (),
} It doesn't look ready for stabilization, but hopefully at some point |
Oh wow, the deref patterns looks great. I think that would indeed be a very nice solution here. |
When creating new abstractions on top of lexopt (like subcommands) it's annoying that
Arg
borrows fromParser
. It means you can't call.value()
or pass the parser to another function until you've unpacked theArg
.The borrowing is necessary to be able to match on
Arg::Long
in an ergonomic way. You can match a&str
against a string literal but not aString
. So theParser
owns the string and theArg
just borrows it.If it ever becomes possible to match a
String
against a string literal then it might be worth releasing a new backward-incompatible version of lexopt to take advantage of that.In the meantime there's an ugly trick you can use to decouple the
Arg
from theParser
:unleash_arg
could be made into a method onArg
, but there might be a better solution.(See also: #3)
The text was updated successfully, but these errors were encountered: