Skip to content
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

Lifetimes of App, Arg, ArgGroup: removing or relaxing #1041

Closed
Rupsbant opened this issue Sep 7, 2017 · 14 comments · Fixed by #4103
Closed

Lifetimes of App, Arg, ArgGroup: removing or relaxing #1041

Rupsbant opened this issue Sep 7, 2017 · 14 comments · Fixed by #4103
Assignees
Labels
A-builder Area: Builder API C-enhancement Category: Raise on the bar on expectations E-medium Call for participation: Experience needed to fix: Medium / intermediate M-breaking-change Meta: Implementing or merging this will introduce a breaking change.
Milestone

Comments

@Rupsbant
Copy link

Rupsbant commented Sep 7, 2017

Affected Version of clap

All 2.x. Breaking API change

Expected Behavior Summary

I want to decouple groups of options to several structs. I want to have a pair functions: one that adds arguments to an App with generated names, one that generates a structure with the added arguments.

Actual Behavior Summary

Uncompilable: the types don't allow to create String in a function and return a borrow.

Sample code

struct A {
...
}
impl A {
  fn arguments<'a, 'b>(prefix: &str) -> Vec<Arg<'a, 'b>> {
    let name = format!("{}.name", prefix);
    ...
  }
}

Proposed solution

Since clap is dependent on its speed it's probably not an option to force 'String' instead of '&str'. However I think Cow should give enough options.

Alternative for me

Create a struct AParse to contain the owned strings, split the function arguments in two parts. A can generate an AParsestruct before creating the App object.

@kbknapp
Copy link
Member

kbknapp commented Sep 14, 2017

This is a focus of 3.x so stay tuned. I've been able to get some work done in spurts during lulls in my day job (which have been few and far between this year). Watch the 3x-master branch for details 😉

@kbknapp kbknapp added the W: 3.x label Sep 14, 2017
@kbknapp
Copy link
Member

kbknapp commented Nov 12, 2017

Personally, I'd like to move away from storing &strs inside the Args as the name. This would solve lifetime issues as I want 3.x to be able to remove the Arg|App<'a, 'b> lifetimes. This could be solved by moving to owned Strings which takes a slight performance hit.

History

clap has two lifetimes for both Arg and App which reference the name, or key of the struct and data related to displaying of the help message or errors. clap uses name and key interchangeably.

clap used to only accept &'static strs because that's what 99% of users use. However, some users do dynamically create args and such, so we moved to &'key str and &'data str to refer to the name/key and display data. There reason these two are separate is because tying them to the same lifetime was cumbersome when one wanted to use 'static for one, but dynamically mess with the other.

Why not use String?

clap originally could have used an owned String to hold both the name and data, however for performance reasons we elected not to. I'm considering moving to String for the name/key as these are typically small, but am reluctant to use String for the data piece as this could be huge and I have yet to see someone who uses anything other than &'static str for such data.

Moving Forward

I want to get away from using &str for the name/key as this is preventing several key features like the ability to create no-* args on the fly.

We could move to Cow<'static, str> for data piece, but so far all attempts to do this have been a hassle. However, I'm OK with it being a hassle if the end result for users is better.

I'm open to ideas.

@kbknapp kbknapp added this to the v3-alpha1 milestone Feb 2, 2018
@kbknapp kbknapp self-assigned this Jul 22, 2018
@kbknapp kbknapp added C-enhancement Category: Raise on the bar on expectations M-breaking-change Meta: Implementing or merging this will introduce a breaking change. P2: need to have E-hard Call for participation: Experience needed to fix: Hard / a lot labels Jul 22, 2018
rwe added a commit to rwe/pastel that referenced this issue Aug 30, 2019
`osascript` is the built-in automation program in macOS.

The method of triggering the macOS color picker from `osascript` is documented here:
https://developer.apple.com/library/archive/documentation/LanguagesUtilities/Conceptual/MacAutomationScriptingGuide/PromptforaColor.html#//apple_ref/doc/uid/TP40016239-CH86-SW1

There is a weirdness of `osascript` in that `console.log` only writes to
`stderr` regardless of the documented "write to stdout" flag.
A workaround was found here and linked in the relevant comment:
https://apple.stackexchange.com/a/278395

The picker is only attempted when `#[cfg(target_os = "macos")]`.
However it's still listed in the "Supported tools" help text because
`clap` make it somewhat hard to customize that help text (e.g.
generating from the list of tools) because it does not take ownership of
strings. Related: clap-rs/clap#1041
sharkdp pushed a commit to sharkdp/pastel that referenced this issue Sep 1, 2019
`osascript` is the built-in automation program in macOS.

The method of triggering the macOS color picker from `osascript` is documented here:
https://developer.apple.com/library/archive/documentation/LanguagesUtilities/Conceptual/MacAutomationScriptingGuide/PromptforaColor.html#//apple_ref/doc/uid/TP40016239-CH86-SW1

There is a weirdness of `osascript` in that `console.log` only writes to
`stderr` regardless of the documented "write to stdout" flag.
A workaround was found here and linked in the relevant comment:
https://apple.stackexchange.com/a/278395

The picker is only attempted when `#[cfg(target_os = "macos")]`.
However it's still listed in the "Supported tools" help text because
`clap` make it somewhat hard to customize that help text (e.g.
generating from the list of tools) because it does not take ownership of
strings. Related: clap-rs/clap#1041
@pksunkara pksunkara modified the milestones: v3-alpha.2, v3.0 Feb 1, 2020
@pksunkara pksunkara removed the W: 3.x label May 26, 2021
@pksunkara pksunkara removed this from the 3.0 milestone May 26, 2021
@epage
Copy link
Member

epage commented Jul 6, 2021

I wonder if kstring would be a good fit. Internally, its an Enum with variants for

  • Owned String
  • &'static str
  • Inline strings, when small

I implemented this for liquid template engine for generating the data context to pass in when rendering. I had a similar situation where most data was 'static but not all. I wanted a more ergonomic Cow. I then also threw in small string optimization to bypass String where possible, because most of my strings were short.

@pksunkara pksunkara added this to the 4.0 milestone Jul 6, 2021
@pksunkara
Copy link
Member

@epage Thanks for pointing it out. Do you know if there are any performance changes?

@epage
Copy link
Member

epage commented Jul 6, 2021

I just ran some of the benchmarks to double check.

For a rough comparison, it looks like Cow::Borrowed < KString::from_static < KString::from_string (small string, 16 or less) < String::from < KString::from_string (large string, 17+)

In terms of memory usage:

---- cow::test::test_size stdout ----
Cow: 32
KStringCow: 32

---- r#ref::test::test_size stdout ----
str: 16
KStringRef: 24

---- string::test::test_size stdout ----
String: 24
Box<str>: 16
Box<Box<str>>: 8
KString: 24

@pksunkara
Copy link
Member

My bad. Let me rephrase, I am concerned about the performance issues related to running time because of the indirections introduced by kstring or similar such library. Also pure Cow implementation has that IIUC (#2504).

I haven't actually worked with something like this before, so my entire theory can be wrong.

@epage
Copy link
Member

epage commented Jul 6, 2021

My first comment was meant to give a rough idea of what runtime performance looks like compared to other options according to kstrings benchmarks. If there is interest in this direction, I can go implement support and run clap's benchmarks. I didn't want to spend time on something if there wasn't interest.

@pksunkara
Copy link
Member

You don't have to implement it for clap and run the benchmarks. What I am looking for is a small bench implemented purely on a struct containing a string with large number of iterations doing something which involves accessing the string inside the struct and cloning the struct.

  • With a lifetime str:

    struct Test<'a> {
      inner: &'a str;
    }
    
  • Non-ergonomic with cow:

    struct Test<'a> {
      inner: Cow<'a, str>;
    }
    
  • With kstring

Once we have these, if we make sure that the performance is not too bad, we can go ahead with implementation.


If the performance is concerning, we can still implement it but should. provide cargo features to let user pick and choose. I haven't looked into kstring, but hopefully it allows something like this.

@epage
Copy link
Member

epage commented Jul 8, 2021

cobalt-org/kstring#6 has the code used for the benchmarks as well as the numbers with some basic analysis.

The summary is:

  • clone of strings <=16 bytes, kstring is faster than String but slower than Cow::Borrowed
  • clone of strings >16 bytes, kstring is slower than Cow::Owned
  • Derefs are pretty much the same across the board.

For clone there seems to be a 10ns overhead that, if it can be diagnosed and fixed, would close the gap with Cow. I suspect this is fixable or else we would see this same slowdown on deref.

EDIT: I've not updated my charts above but I have dropped the overhead from 10ns to 5ns by not inlining clone(). My guess is we were thrashing the icache.

@epage epage added the E-medium Call for participation: Experience needed to fix: Medium / intermediate label Dec 9, 2021
@epage
Copy link
Member

epage commented Feb 2, 2022

When we do this, we should keep the workarounds in #1104 in mind and have an ToId trait that we use on builder functions that can be implemented by an enum for type chekcing.

epage added a commit to epage/clap that referenced this issue Aug 12, 2022
This is a part of clap-rs#2870 and is prep for clap-rs#1041

Oddly enough, this dropped the binary size by 200 Bytes

Compared to `HEAD~` on `06_rustup`:
- build: 6.21us -> 6.23us
- parse: 7.55us -> 8.17us
- parse_sc: 7.95us -> 7.65us
epage added a commit to epage/clap that referenced this issue Aug 12, 2022
This is a step towards clap-rs#1041
- `ArgGroup` no longer takes a lifetime
- One less field type needs a lifetime

For now, we are using a more brute force type (`String`) so we can
establish performance base lines.  I was torn on whether to use `&str`
everywhere or make an `IdRef`.  The latter would add a lot of noise that
I'm concerned about, so i left it simple for now.  `IdRef` would help to
communicate the types involved though.

Speaking of communicating types, I'm also torn on whether we should use
`Id` for all strings or if we should have `Id`, `Name`, etc types to
avoid people mixing and matching.

This added 18.7 KB.

Compared to `HEAD~` on `06_rustup`:
- build: 6.23us -> 7.41us
- parse: 8.17us -> 9.36us
- parse_sc: 7.65us -> 9.29us
epage added a commit to epage/clap that referenced this issue Aug 15, 2022
The main breakinge change cases:
- `&[char]`: now requires removing `&`
- All other non-ID `&[_]`: hopefully clap-rs#1041 will make these non-breaking

Fixes clap-rs#2870
epage added a commit to epage/clap that referenced this issue Aug 16, 2022
This helps with
- API cleanup by not having ambigious `None`, see clap-rs#950
- Removes ambiguity with `None` when using owned/borrowed types for
  clap-rs#1041
epage added a commit to epage/clap that referenced this issue Aug 17, 2022
This is a part of clap-rs#1041

Because `Option<Into<T>>` is ambiguous for `None`, we had to create
`Resettable` to workaround it.
epage added a commit to epage/clap that referenced this issue Aug 17, 2022
Another step towards clap-rs#1041

This isn't the long term type for `PossibleValue::help`, I just wanted
to get the lifetime out of the way first before figuring out how help
will work.
epage added a commit to epage/clap that referenced this issue Aug 19, 2022
This is a part of clap-rs#1041

Because `Option<Into<T>>` is ambiguous for `None`, we had to create
`Resettable` to workaround it.
epage added a commit to epage/clap that referenced this issue Aug 19, 2022
Another step towards clap-rs#1041

This isn't the long term type for `PossibleValue::help`, I just wanted
to get the lifetime out of the way first before figuring out how help
will work.
epage added a commit to epage/clap that referenced this issue Aug 19, 2022
Another step towards clap-rs#1041

This isn't the long term type for `PossibleValue::help`, I just wanted
to get the lifetime out of the way first before figuring out how help
will work.
epage added a commit to epage/clap that referenced this issue Aug 19, 2022
Another step towards clap-rs#1041

This isn't the long term type for `PossibleValue::help`, I just wanted
to get the lifetime out of the way first before figuring out how help
will work.
epage added a commit to epage/clap that referenced this issue Aug 22, 2022
Impact:
- Binary size: 556.6 KiB to 578.4 KiB
- build time: 6.4950 us (7% slower)
- parse time: 7.7256 us
- parse sc time: 8.1580 us (5% faster)

Fixes clap-rs#1041
Fixes clap-rs#2150
epage added a commit to epage/clap that referenced this issue Aug 22, 2022
Impact:
- Binary size: 556.6 KiB to 578.4 KiB
- build time: 6.4950 us (7% slower)
- parse time: 7.7256 us
- parse sc time: 8.1580 us (5% faster)

Fixes clap-rs#1041
Fixes clap-rs#2150
Calder-Ty pushed a commit to Calder-Ty/clap that referenced this issue Aug 24, 2022
This helps with
- API cleanup by not having ambigious `None`, see clap-rs#950
- Removes ambiguity with `None` when using owned/borrowed types for
  clap-rs#1041
Calder-Ty pushed a commit to Calder-Ty/clap that referenced this issue Aug 24, 2022
This is a part of clap-rs#1041

Because `Option<Into<T>>` is ambiguous for `None`, we had to create
`Resettable` to workaround it.
Calder-Ty pushed a commit to Calder-Ty/clap that referenced this issue Aug 24, 2022
Another step towards clap-rs#1041

This isn't the long term type for `PossibleValue::help`, I just wanted
to get the lifetime out of the way first before figuring out how help
will work.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-builder Area: Builder API C-enhancement Category: Raise on the bar on expectations E-medium Call for participation: Experience needed to fix: Medium / intermediate M-breaking-change Meta: Implementing or merging this will introduce a breaking change.
Projects
None yet
4 participants