Hi there - I've been beating my head against this one a little - I've got some fn that takes some type T that implements AsRef<[AsRef]>, and now in some code I want to hold onto one of those in a struct and pass it to that function when the struct is dropped - however, I want to collect it to a Vec and append to it, in the event that something happens (in this case, a coercion from u128 to i64 fails)
use std::time::Instant;
use std::convert::TryInto;
pub trait TagsProvider<S>
where
Self: AsRef<[S]>,
S: AsRef<str>,
{
}
impl<S, T> TagsProvider<S> for T
where
T: AsRef<[S]>,
S: AsRef<str>,
{
}
fn foo<S: AsRef<str>>(metric: S, ms: i64, tags: impl TagsProvider<S>) {
println!("{:?}:{:?}", metric.as_ref(), ms);
tags.as_ref().iter().for_each(|v| println!("{:?}", v.as_ref()))
}
pub struct TimingGuard<S, P>
where
S: AsRef<str>,
P: TagsProvider<S>,
{
metric: S,
start: Instant,
tags: P
}
impl<S, P> TimingGuard<S, P>
where
S: AsRef<str>,
P: TagsProvider<S>,
{
pub fn new(metric: S, tags: P) -> Self {
Self {
metric,
start: Instant::now(),
tags,
}
}
}
impl<S, P> Drop for TimingGuard<S, P>
where
S: AsRef<str>,
P: TagsProvider<S>,
{
fn drop(&mut self) {
let elapsed = self.start.elapsed();
let Ok(ms): Result<i64, _> = elapsed.as_millis().try_into() else {
let mut tags: Vec<_> = self.tags.as_ref().iter().map(|t| t.as_ref()).collect();
tags.push("overflowed");
foo(self.metric, i64::MAX, tags);
return;
};
foo(self.metric, ms, self.tags);
}
}
(link to playground: Rust Playground)
The reason I worry this might be a compiler bug is that, if I tear out all the generics in TimingGuard and have it just hold a Vec, this code compiles, but it breaks other previously-compiling code somewhere else in the codebase this is from, which is passing an Vec to foo... which is "spooky action at a distance" enough for me to be concerned
Apologies if I am just missing something obvious, but this has really stumped me!