9
312
Fork
You've already forked codeberg-cli
33

Remove needless string allocations #243

Open
opened 2025年10月06日 09:05:42 +02:00 by Aviac · 1 comment
Owner
Copy link

At a lot of places we should get away with just using properly lifetimed &str instead of String.

Go through the code and fix all the cases where this happens.

At a lot of places we should get away with just using properly lifetimed `&str` instead of `String`. Go through the code and fix all the cases where this happens.
Contributor
Copy link

I had the AI analyse the codebase and it found several patterns causing unnecessary string allocations:

Main Issues

  1. option_display function (85+ usages across 23 files)

    • Returns String but is often used inside format!() macros, causing double allocation
    • Uses String::from("?") instead of "?" for the None case
  2. Unnecessary String::from() in closures (~10 occurrences)

    • Pattern: .unwrap_or_else(|| String::from("?"))
    • Can be simplified to .unwrap_or("?")
  3. String literals in table construction

    • String::from("Title") where &str might suffice (depends on table API)

Proposed Solutions

Option A: Use Cow<'static, str>

usestd::borrow::Cow;pubfn option_display<T: ToString>(opt: &Option<T>)-> Cow<'static,str>{opt.as_ref().map(|t|Cow::Owned(t.to_string())).unwrap_or(Cow::Borrowed("?"))}

Pros:

  • Only allocates when Some, returns borrowed "?" for None
  • Drop-in replacement due to Deref implementation
  • Works seamlessly with format!() and other string operations
  • Minimal API changes

Cons:

  • Still allocates for the Some case (unavoidable since we need ToString)

Option B: Display trait wrapper

pubstruct OptionDisplay<'a,T>(&'aOption<T>);impl<'a,T: ToString>DisplayforOptionDisplay<'a,T>{fn fmt(&self,f: &mutFormatter<'_>)-> fmt::Result{match&self.0{Some(v)=>write!(f,"{}",v.to_string()),None=>write!(f,"?"),}}}pubfn option_display<T>(opt: &Option<T>)-> OptionDisplay<'_,T>{OptionDisplay(opt)}

Pros:

  • Zero allocations until actually formatted
  • Most efficient solution

Cons:

  • More complex lifetime management
  • May require changes where the result is stored/passed around
  • Could break existing usage patterns

Recommendation

Option A (Cow) provides immediate benefits with minimal disruption. We can then address the remaining allocations in the table construction separately if needed.

Happy to implement whichever approach you prefer, or explore other solutions if you have different ideas.

I had the AI analyse the codebase and it found several patterns causing unnecessary string allocations: ## Main Issues 1. **`option_display` function** (85+ usages across 23 files) - Returns `String` but is often used inside `format!()` macros, causing double allocation - Uses `String::from("?")` instead of `"?"` for the None case 2. **Unnecessary `String::from()` in closures** (~10 occurrences) - Pattern: `.unwrap_or_else(|| String::from("?"))` - Can be simplified to `.unwrap_or("?")` 3. **String literals in table construction** - `String::from("Title")` where `&str` might suffice (depends on table API) ## Proposed Solutions ### Option A: Use `Cow<'static, str>` ```rust use std::borrow::Cow; pub fn option_display<T: ToString>(opt: &Option<T>) -> Cow<'static, str> { opt.as_ref() .map(|t| Cow::Owned(t.to_string())) .unwrap_or(Cow::Borrowed("?")) } ``` **Pros:** - Only allocates when `Some`, returns borrowed `"?"` for `None` - Drop-in replacement due to `Deref` implementation - Works seamlessly with `format!()` and other string operations - Minimal API changes **Cons:** - Still allocates for the `Some` case (unavoidable since we need `ToString`) ### Option B: `Display` trait wrapper ```rust pub struct OptionDisplay<'a, T>(&'a Option<T>); impl<'a, T: ToString> Display for OptionDisplay<'a, T> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match &self.0 { Some(v) => write!(f, "{}", v.to_string()), None => write!(f, "?"), } } } pub fn option_display<T>(opt: &Option<T>) -> OptionDisplay<'_, T> { OptionDisplay(opt) } ``` **Pros:** - Zero allocations until actually formatted - Most efficient solution **Cons:** - More complex lifetime management - May require changes where the result is stored/passed around - Could break existing usage patterns ## Recommendation Option A (Cow) provides immediate benefits with minimal disruption. We can then address the remaining allocations in the table construction separately if needed. Happy to implement whichever approach you prefer, or explore other solutions if you have different ideas.
Sign in to join this conversation.
No Branch/Tag specified
main
v0.5.2
v0.5.1
v0.5.0
v0.4.11
v0.4.10
v0.4.9
v0.4.8
v0.4.7
v0.4.6
v0.4.5
v0.4.4
v0.4.3
v0.4.1
v0.4.2
v0.4.0
v0.3.5
v0.3.4
v0.3.2
v0.3.1
v0.3.0
v0.2.1
v0.2.0
v0.1.1
v0.1.0
Milestone
Clear milestone
No items
No milestone
Projects
Clear projects
No items
No project
Assignees
Clear assignees
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Aviac/codeberg-cli#243
Reference in a new issue
Aviac/codeberg-cli
No description provided.
Delete branch "%!s()"

Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?