struct Criterion {
// ...
}
// This trait definition will not compile unless I add a 'static bound to it,
// apparently because it doesn't want a &'static reference to a type that has
// a non-'static generic parameter (the argument to the function).
// This is an unacceptable limitation for my use case.
// Is there some workaround I can use?
trait SomeTrait {
const CASES: &[(Criterion, fn(Self) -> ReturnType)];
}
fn transform<T: SomeTrait>(value: T, request: String) -> ReturnType {
for (criterion, transform_func) in T::CASES {
if criterion.matches(&request) {
return (transform_func)(value);
}
}
ReturnType::default()
}
There's no safe way to "disable" the outlives check as it would be unsound in the general case.
I think what you need here is a way for the reference in the const
to be non-'static
. generic_const_items
can do it but that's an unstable, incomplete feature.[1]
Edit: Here's a way to define the const
for all lifetimes 'a
such that Self: 'a
on stable.
(That's the same playground as my comment two posts later.)
Click for original failed attempts and suggestions
I attempted to mimic a GAT work around instead:
trait SomeTraitLifetime<'a, Guard = &'a Self>: 'a /* (1) */ {
const CASES: &'a [(Criterion, fn(Self) -> ReturnType)]
// (2)
where Self: 'a,
;
}
trait SomeTrait: for<'a> SomeTraitLifetime<'a> {}
impl<T: ?Sized + for<'a> SomeTraitLifetime<'a>> SomeTrait for T {}
But this didn't work as one of (1)
or (2)
is required to compile, and
I may have missed a way to make those work.
But perhaps a better An alternative approach is to drop the requirement to use a const
and instead use:
trait SomeTrait {
fn cases<'a>() -> &'a [(Criterion, fn(Self) -> ReturnType)]
where
Self: 'a,
;
}
Although you do lose the compile time nature of the const
this way.
Edit: The suggestion in my next post is better IMO, but I don't know if everyone would share that opinion.
Maybe I got too caught up on the "for any" in my GAT mimicking attempts. You can also just put a lifetime on the trait and use it directly:
trait SomeTrait<'a>: 'a {
const CASES: &'a [(Criterion, fn(Self) -> ReturnType)];
}
fn transform<'t, T: SomeTrait<'t>>(value: T, request: String) -> ReturnType {
for (criterion, transform_func) in T::CASES {
if criterion.matches(&request) {
return (transform_func)(value);
}
}
ReturnType::default()
}
impl<'a, 'i: 'a> SomeTrait<'a> for &'i () {
const CASES: &'a [(Criterion, fn(Self) -> ReturnType)] = &[
(Criterion {}, |_| {})
];
}
Though it does make the lifetime infect everything.
Sorry for the spam, I keep realizing I'm not at a dead end. Here's a for<'a>
version that works. (I've edited my first reply to contain link to this solution too.)
trait SomeTraitLifetime<Cases> {
const CASES: Cases;
}
trait SomeTrait: for<'a>
SomeTraitLifetime<&'a [(Criterion, fn(Self) -> ReturnType )]>
{}
impl<T> SomeTrait for T where ... {}
// Non-`'static` implementor. It's important to not put any bounds
// involving `'a` on the implementation or `for<'a> SomeTraitLifetime<'a>`
// may not hold.
impl<'a, 'i> SomeTraitLifetime<&'a [(Criterion, fn(Self) -> ReturnType)]> for &'i () {
const CASES: &'a [(Criterion, fn(Self) -> ReturnType)] = &[];
}