🦀 Rust Guard Improvement Report
Improvement 1: Change PathLabelResult.items_path from Option<String> to Option<&'static str>
Category: Type Safety
File(s): guards/github-guard/rust-guard/src/labels/response_paths.rs, guards/github-guard/rust-guard/src/lib.rs
Effort: Small (< 15 min)
Risk: Low
Problem
PathLabelResult.items_path is declared as Option<String> in response_paths.rs (line 27), and PathLabeledOutput.items_path is the same type in lib.rs (line 243). However, every value stored in this field comes from extract_items_array, which returns &'static str. The three assignment sites all call .to_string() on that static string, creating unnecessary heap allocations:
response_paths.rs:213-216 items_path: if items_path.is_empty() { None } else { Some(items_path.to_string()) }
response_paths.rs:311-314 items_path: if items_path.is_empty() { None } else { Some(items_path.to_string()) }
response_paths.rs:628-631 items_path: if items_path.is_empty() { None } else { Some(items_path.to_string()) }
The declared type is lying to the reader — the data is always a compile-time string constant, not a heap-allocated string.
Suggested Change
- Change
PathLabelResult.items_path in response_paths.rs from Option<String> to Option<&'static str>
- Update the three assignment sites to use
Some(items_path) directly (no more .to_string())
- In
lib.rs, change PathLabeledOutput.items_path from Option<String> to Option<&'static str>, and update the assignment at line 815 accordingly (Serde's Serialize handles &'static str without issue)
Before
// response_paths.rs line 27
pub struct PathLabelResult {
pub labeled_paths: Vec<PathLabelEntry>,
pub default_labels: Option<crate::ResourceLabels>,
pub items_path: Option<String>, // <- heap allocation for a static string
}
// Three identical sites (lines 213-216, 311-314, 628-631):
items_path: if items_path.is_empty() {
None
} else {
Some(items_path.to_string()) // <- to_string() on &'static str
},
After
// response_paths.rs line 27
pub struct PathLabelResult {
pub labeled_paths: Vec<PathLabelEntry>,
pub default_labels: Option<crate::ResourceLabels>,
pub items_path: Option<&'static str>, // <- zero-cost: statically known
}
// Three sites simplify to:
items_path: if items_path.is_empty() {
None
} else {
Some(items_path) // <- borrow, no allocation
},
// lib.rs PathLabeledOutput also changes:
struct PathLabeledOutput {
labeled_paths: Vec<PathLabel>,
#[serde(skip_serializing_if = "Option::is_none")]
default_labels: Option<ResourceLabels>,
#[serde(skip_serializing_if = "Option::is_none")]
items_path: Option<&'static str>, // <- no String allocation before serialization
}
Why This Matters
- Type accuracy: the type now reflects what the data actually is — a compile-time constant
- Eliminates 3 heap allocations on every collection labeling call (list_pull_requests, list_issues, list_project_items)
- Simplifies the 3 assignment sites: the
if is_empty() ... else Some(.to_string()) pattern collapses to if is_empty() ... else Some(items_path)
- In WASM where every allocation has a cost, removing unnecessary
String boxing from a hot path is meaningful
Improvement 2: Use Cow<'_, str> for baseline_scope in apply_tool_labels
Category: Performance
File(s): guards/github-guard/rust-guard/src/labels/tool_rules.rs
Effort: Small (< 15 min)
Risk: Low
Problem
apply_tool_labels (line 125) declares baseline_scope as String:
let mut baseline_scope = repo_id.to_string();
The variable is then reassigned 8 times using .to_string() on &'static str constants from scope_names:
line 464: baseline_scope = scope_names::USER.to_string();
line 485: baseline_scope = scope_names::GITHUB.to_string();
line 501: baseline_scope = scope_names::GITHUB.to_string();
line 515: baseline_scope = scope_names::GITHUB.to_string();
line 587: baseline_scope = scope_names::GITHUB.to_string();
line 681: baseline_scope = scope_names::GITHUB.to_string();
line 690: baseline_scope = scope_names::GITHUB.to_string();
line 717: baseline_scope = scope_names::USER.to_string();
Each of these allocates a new heap String from a string that already exists as a static constant. Only a few branches genuinely require an owned value (when baseline_scope is set from s_repo_id.clone() or owner.clone()). The variable is only ever consumed at line 728 via &baseline_scope (a deref coercion to &str).
Suggested Change
Change baseline_scope to Cow<'_, str>. Static-constant assignments become Cow::Borrowed; owned-string assignments become Cow::Owned. Add use std::borrow::Cow; to tool_rules.rs imports.
Before
use super::constants::{field_names, scope_names, SENSITIVE_FILE_KEYWORDS, SENSITIVE_FILE_PATTERNS};
// ...
let mut baseline_scope = repo_id.to_string();
// ... later in multiple match arms:
baseline_scope = scope_names::USER.to_string(); // alloc
baseline_scope = scope_names::GITHUB.to_string(); // alloc
// ...
baseline_scope = s_repo_id.clone(); // alloc
baseline_scope = owner.clone(); // alloc
// Used only as a &str:
ensure_integrity_baseline(&baseline_scope, integrity, ctx)
After
use std::borrow::Cow;
use super::constants::{field_names, scope_names, SENSITIVE_FILE_KEYWORDS, SENSITIVE_FILE_PATTERNS};
// ...
let mut baseline_scope: Cow<'_, str> = Cow::Borrowed(repo_id);
// Static assignments — zero allocation:
baseline_scope = Cow::Borrowed(scope_names::USER);
baseline_scope = Cow::Borrowed(scope_names::GITHUB);
// Owned assignments — allocation only where actually needed:
baseline_scope = Cow::Owned(s_repo_id); // was s_repo_id.clone()
baseline_scope = Cow::Owned(owner.clone());
// Call site unchanged — Cow<str> derefs to &str:
ensure_integrity_baseline(&baseline_scope, integrity, ctx)
Why This Matters
- Eliminates 8 heap allocations per
apply_tool_labels invocation on common paths (list_gists, dismiss_notification, get_me, get_teams, etc.)
scope_names::USER and scope_names::GITHUB are &'static str — there is no reason to ever heap-allocate them
Cow::Borrowed is a zero-cost wrapper; the call site &baseline_scope is unchanged (deref coercion works identically)
- In WASM this matters: allocator calls are the most expensive operation per byte of overhead
Codebase Health Summary
- Total Rust files: 9
- Total lines: 13,774
- Areas analyzed: lib.rs, tools.rs, labels/mod.rs, labels/backend.rs, labels/constants.rs, labels/helpers.rs, labels/response_items.rs, labels/response_paths.rs, labels/tool_rules.rs
- Areas with no further improvements: tools.rs (BLOCKED_TOOLS const already added; operation predicate tests already suggested)
Generated by Rust Guard Improver • Run: §25791958478
Generated by Rust Guard Improver · ● 1.1M · ◷
🦀 Rust Guard Improvement Report
Improvement 1: Change
PathLabelResult.items_pathfromOption<String>toOption<&'static str>Category: Type Safety
File(s):
guards/github-guard/rust-guard/src/labels/response_paths.rs,guards/github-guard/rust-guard/src/lib.rsEffort: Small (< 15 min)
Risk: Low
Problem
PathLabelResult.items_pathis declared asOption<String>inresponse_paths.rs(line 27), andPathLabeledOutput.items_pathis the same type inlib.rs(line 243). However, every value stored in this field comes fromextract_items_array, which returns&'static str. The three assignment sites all call.to_string()on that static string, creating unnecessary heap allocations:The declared type is lying to the reader — the data is always a compile-time string constant, not a heap-allocated string.
Suggested Change
PathLabelResult.items_pathinresponse_paths.rsfromOption<String>toOption<&'static str>Some(items_path)directly (no more.to_string())lib.rs, changePathLabeledOutput.items_pathfromOption<String>toOption<&'static str>, and update the assignment at line 815 accordingly (Serde'sSerializehandles&'static strwithout issue)Before
After
Why This Matters
if is_empty() ... else Some(.to_string())pattern collapses toif is_empty() ... else Some(items_path)Stringboxing from a hot path is meaningfulImprovement 2: Use
Cow<'_, str>forbaseline_scopeinapply_tool_labelsCategory: Performance
File(s):
guards/github-guard/rust-guard/src/labels/tool_rules.rsEffort: Small (< 15 min)
Risk: Low
Problem
apply_tool_labels(line 125) declaresbaseline_scopeasString:The variable is then reassigned 8 times using
.to_string()on&'static strconstants fromscope_names:Each of these allocates a new heap
Stringfrom a string that already exists as a static constant. Only a few branches genuinely require an owned value (whenbaseline_scopeis set froms_repo_id.clone()orowner.clone()). The variable is only ever consumed at line 728 via&baseline_scope(a deref coercion to&str).Suggested Change
Change
baseline_scopetoCow<'_, str>. Static-constant assignments becomeCow::Borrowed; owned-string assignments becomeCow::Owned. Adduse std::borrow::Cow;totool_rules.rsimports.Before
After
Why This Matters
apply_tool_labelsinvocation on common paths (list_gists, dismiss_notification, get_me, get_teams, etc.)scope_names::USERandscope_names::GITHUBare&'static str— there is no reason to ever heap-allocate themCow::Borrowedis a zero-cost wrapper; the call site&baseline_scopeis unchanged (deref coercion works identically)Codebase Health Summary
Generated by Rust Guard Improver • Run: §25791958478