prometheus / prometheus/client_rust
Change `Family::new_with_constructor` to be `impl Fn() -> C` instead of function pointer `fn`
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 606
- Forks
- 113
- Avg merge
- 9h 7m
- Merged PRs (30d)
- 8
Description
Currently Family is defined using a fn pointer instead of Fn which is poor design:
pub struct Family<S, M, C = fn() -> M> { }
This means that this fails to compile:
let buckets = vec![1.0, 2.0, 3.0];
let metric = Family::<S, Histogram>::new_with_constructor(move || Histogram::new(buckets.clone()));
even though it the closure passed in implements Fn (though is not a fn() since closures can't be coerced to function pointers if they capture variables).
This becomes problematic when creating helper methods to instantiate metrics. For example I can write this:
/// Helper function to instantiate a `prometheus_client` metric.
fn instantiate_metric_primitive<
S: EncodeLabelSet + Debug + Clone + std::hash::Hash + Eq + Send + Sync + 'static,
M: Debug + Default + EncodeMetric + TypedMetric + Send + Sync + 'static,
>(
registry: &mut Registry,
name: &str,
description: &str,
) -> Family<S, M> {
let metric = Family::<S, M>::default();
tracing::info!("registering {} metric with metrics recorder", name);
registry.register(name, description, metric.clone());
metric
}
But not this:
// helper function to instantiate a histogram metric
fn instantiate_histogram_metric<
S: EncodeLabelSet + Debug + Clone + std::hash::Hash + Eq + Send + Sync + 'static,
>(
registry: &mut Registry,
name: &str,
description: &str,
buckets: Vec<f64>,
) -> Family<S, Histogram> {
let metric =
// closure cannot be coerced into an fn pointer (even though it's `impl Fn`
Family::<S, Histogram>::new_with_constructor(move || Histogram::new(buckets.clone()));
tracing::info!("registering {} metric with metrics recorder", name);
registry.register(name, description, metric.clone());
metric
}
Suggested Fix
The quick fix is to redefine family to be:
pub struct Family<S, M, C: Fn() -> M> { // <-- `C = fn() -> M` became trait bounds`C: Fn() -> M`
metrics: Arc<RwLock<HashMap<S, M>>>,
constructor: C,
}
But in general stating the types in struct definitions is poor design. Prefer to instead refactor Family so there are no bounds on the struct and instead put bounds on the required impls.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at the Family definition and its new_with_constructor implementation, then trace the existing impl bounds and default construction described in the issue. Refactor the constructor type so capturing closures are accepted without breaking existing Family uses; done when the histogram helper example compiles and the current default path still works.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- observability
- Issue type
- Refactor
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100