I'm sorry the title is confusing and perhaps that is indication that my intended design is not ideal.
I'm working on a crate for JSON Schema. As part of my core crate, I have a trait that represents the output of the evaluation (named Report
). In almost all cases, the Report
borrows from the input serde_json::Value
, except in the case of a compilation error. In that scenario, the Report
needs to be owned.
Given that there's no way to indicate Self<'static>
, I'm using an associated type, Owned
, to fill in for the owned flavor, returned from the method into_owned
:
pub trait Report<'v>: Clone + std::error::Error + Serialize + DeserializeOwned {
type Error<'e>: 'e + Serialize + DeserializeOwned;
type Annotation<'a>: 'a + Serialize + DeserializeOwned;
type Output: 'static + Output;
type Owned: 'static
+ Report<
'static,
Error<'static> = Self::Error<'static>,
Annotation<'static> = Self::Annotation<'static>,
Output = Self::Output,
>;
fn into_owned(self) -> Self::Owned;
// snip
}
However, this doesn't work, as the compiler believes the referenced value
is leaking:
fn validate<'v>(
&mut self,
dialect_idx: usize,
value: &'v Value,
) -> Result<(), CompileError<C, K>> {
if !self.validate {
return Ok(());
}
let mut evaluated = HashSet::default();
let mut eval_numbers = Numbers::with_capacity(7);
let key = self.dialects.get_by_index(dialect_idx).unwrap().schema_key;
let report = self.schemas.evaluate(Evaluate {
key,
value,
criterion: &self.criterion,
output: <CriterionReportOutput<C, K>>::verbose(),
instance_location: Pointer::default(),
keyword_location: Pointer::default(),
sources: self.sources,
evaluated: &mut evaluated,
global_numbers: self.numbers,
eval_numbers: &mut eval_numbers,
})?;
if !report.is_valid() {
let report: <C::Report<'v> as Report>::Owned = report.into_owned();
return Err(CompileError::SchemaInvalid {
report,
backtrace: Backtrace::capture(),
});
}
Ok(())
}
}
error: lifetime may not live long enough
--> grill-core/src/schema/compiler.rs:732:25
|
707 | fn validate<'v>(
| -- lifetime `'v` defined here
...
732 | let report: <C::Report<'v> as Report>::Owned = report.into_owned();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'v` must outlive `'static`
I have a repro on the Playground but I'm not certain on it being completely 1-to-1. I'm sorry if that's not the case.
Is this possible? Is there anyway to accomplish this without re-designing everything? I doubt anyone wants to look through the source but its available on GitHub
The Rust forums user quinedot provided this answer (he opted not to post it here himself):
You can put the equality constraint into Criterion instead.
trait Criterion { type Owned: 'static + Report<'static>; type Report<'v>: Report<'v, Owned = <Self as Criterion>::Owned>; } struct CriterionImpl {} impl Criterion for CriterionImpl { type Owned = <ReportImpl<'static> as Report<'static>>::Owned; type Report<'v> = ReportImpl<'v>; } struct Error<C: Criterion> { report: <C as Criterion>::Owned, }