-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
WIP: EXPERIMENT: Move from &'b str to Cow<'static, str> #1728
Conversation
I agree the owned the data which is needed elsewhere is a rare case (and kind of goes against Rust's ownership/borrowing principals anyways), so it shouldn't be a priority. I'd be fine saying this case needs to I'm trying to remember off the top of my head, but I believe the problem I ran into last time I tried moving to My only concern would be using Just some thoughts 😄 |
I agree with Kevin here, Cow would make the API slightly trickier to use (from previous experience of using Cow on another unrelated project). I'd like to take a try at this and see if there's a different way, but post 3.0 release |
So, we will still need one lifetime at the least and use it as I think we can achieve this transition. AFAIK this will improve performance. I don't see why we want to do this post 3.0 |
Yeah, I'm having certain difficulties transiting the internal machinery to Cow. For example, some parts of code work with
(This problem actually relates to TeXitoi/structopt#114) No, we won't need shorter lifetimes. I've looked over #815 and I don't understand why you think we will. I'm given impression that you're thinking about preceding the stored arguments with // somewhere in app._build()
for arg in self.args {
if arg.ovveridable {
let override = arg.clone();
override.long.to_mut().insert_str(0, "--no-");
self.args.push(override);
}
} It's not even bad or too-ineffective option. No shorter lifetimes involved. Alternative (and more sophisticated) way to implement it would be tweaking the parsing logic a bit: // this code is placed just after the `opt starts with --` check
if opt.starts_with("no-") {
let real_opt = parse_opt_name(opt[3..]);
if let Some(flag) = flags.find(real_opt) {
flag.set_to_off();
} else {
// resort back to normal parsing
}
}
Again, no shorter lifetimes in use. (The TeXitoi/structopt#114 prefix problem is somewhat more interesting, but I believe we'll be able to process it just like
Hm, I assume the @Dylan-DPC Would you kindly be more descriptive regarding to your experience with
In any way, the change is breaking, so we either land it before |
Yup, using |
The diff is going to be quite large and most of it will be trivial changes not worthy to waste reviewers' time on, so I'm going to mark really significant parts, the code I'll have had to actually rewrite, with |
clap/src/build/usage_parser.rs Lines 91 to 95 in 2370665
I must confess, the progress has stalled. I encountered a snippet I can't easily deal with. When But, if it would take I can think of a few options:
So I ask of you, the wisest of |
This would be the safer way, wouldn't it? |
Yes, but also the most inefficient. I would go for the first one if it was just up to me (I've never seen |
Wouldn't loading from YAML do that? |
YAML loader has nothing to do with |
If the owned |
None | ||
}; | ||
pub fn arg<A: Into<Arg>>(mut self, a: A) -> Self { | ||
let help_heading: Option<Cow<'static, str>> = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be good to tag as_deref
to be used for rust 1.40.0 later on?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand what as_deref
has to do with the bit of code you highlighted. Could you please elaborate?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something like
let help_heading: Option<Cow<'static, str>> = self.help_headings.last().map(Deref::deref);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True, thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True, thanks!
pub fn long<T: Into<Cow<'static, str>>>(mut self, l: T) -> Self { | ||
// NEED_REVIEW | ||
self.long = Some(match l.into() { | ||
Cow::Borrowed(s) => Cow::Borrowed(s.trim_start_matches(|c| c == '-')), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just directly trim "--"
from the start since long must be --
IIRC?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here is the deal: you give me the working code that does exactly what you said, and I put your credentials to the codebase, accompanied by praises and immodest compliments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think @pickfire means s.trim_start_matches("--")
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@CreepySkeleton credentials? Wow! Looks interesting but I don't get what you meant.
@pksunkara Correct, s.trim_start_matches("--")
, just wondering why not this one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just wondering why not this one?
Because we would like to strip all the dashes. Your variant will leave one dash in case there's odd number of them.
credentials? Wow! Looks interesting but I don't get what you meant.
Your nickname, email, name, whatever you'd prefer to be adressed with; I recommend something in the spirit of "The Emperor of Programming and Button-pressing in general".
I've got a challenge for you: try to make this code compile. That should give you the understanding of the problem. |
Looks hard. Sorry, now I understand why. But I did it by changing the parameter https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=799567fdeee5beaa9c997fe4d8c5b6f3 |
Unfortunately, we can't afford I believe the solutions I presented above are the only solutions we can work with, and each of them has its own drawbacks:
We just need to decide which penalty we can live with, and both @pksunkara and I think that copying is the best bet. |
I vote copying, if you want perf, it's simple enough to avoid this particular feature. |
@CreepySkeleton Isn't cloning instead of copying? |
I used |
@CreepySkeleton Yes, but copying are usually much cheaper than cloning. |
@CreepySkeleton Would it be good to also have support for https://github.com/maciejhirsz/beef to have faster and smaller alternative to |
Or if we want to reduce memory usage to prevent too many strings of the same version, we could use string interner. https://matklad.github.io/2020/03/22/fast-simple-rust-interner.html |
@pickfire I wasn't using "copying" in Rust " Well, maybe Strings interning is heavily specialized for "there are lots of copies of the same sting, and all we ever need is to check if two strings are equal". The former is not the case at all here. Anyway, the bad streak in my life is almost over; I'm going to get back to coding on April, 2. But I feel like I should be striving for bugfixing first and pushing the new release. |
This is long abandoned. Maybe I'll make more successful attempt one day. |
Yes, this is the most straightforward solution, and the one I would probably go with. my point was that we can't really share owned variants, even if they never change, we have to reallocate and copy. And this is what you do. |
"they never change" doesn't mean "they never drop" right? To share such owned variants, additional `'b' lifetime will be introduced, which is exactly what we want to forbid. So reallocating and copying is unforbiddable I guess? So is there any other road-blocker? |
Yeah, right. It's not a big deal anyway.
Apart from that the fact this is going to be a very big amount of very mundane work? None. |
OK, this is the very early stage of the ambitious "get rid of lifetimes" plan.
As @pksunkara and I have discussed, it should be possible to remove lifetimes entirely by making use of
Cow<'static, str/OsStr>
instead of plain references. This would be very useful for those who generate their data at runtime, as described in #1041.The plan is simple: turn
fn method(self, data: &'b str)
interface intofn method<T: Into<Cow<'static, str>>>(self, data: T)
. Since all of&str
,&OsStr
,String
,OsString
implementInto<Cow>
, the transition should be more or less painless for our users.There are three ways people could possibly build
clap::App
:&'static str
), explicitly written asapp.help("some string")
. Those would be turned intoCow::Borrowed
, involving no clones.String
), the data is not needed anywhere else. Those would be turned intoCow::Owned(s)
by moving the string into the app, no cloning would be involved either.String
), the data is needed somewhere else. The "out of luck" case, users would need to explicitly clone the data. For what it's worth, I've never seen such a usage in the wild.Well, let's make it build and see the benches.
Closes #1041
List of significant changes I've made:
&[&str]
) into iterator basedList of TODOs:
std::borrow::Cow