A "how can I do this today" question is a better fit for the users forum. The internals forum is better fit for "how can we improve doing this in the future."
For the purpose of future extensions, it's theoretically possible that Fn
and FnMut
could be GAT-ified. However, since they also imply that a closure is also FnOnce
, it's not really possible for them to be used directly; there's no way for FnOnce
to return borrowed state. New traits are a possibility, but lifetime inference will still be a hard problem.
For right now, since your desired shape is fn(&self) -> &T
, it's interesting to note that this is the shape Deref
takes. You should be able to define a type along the lines of
struct Lend<T, F, O: ?Sized>(T, F)
where
F: Fn(&T) -> &O;
impl<T, F, O: ?Sized> Deref for Lend<T, F, O>
where
F: Fn(&T) -> &O,
{
type Target = O;
fn deref(&self) -> &O {
(self.1)(&self.0)
}
}
which allows you to make an adhoc Deref
item able to return references to the first "state capture" part, using it like
pub fn path_to_example_nifti() -> impl Deref<Target = str> {
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
// ...
Lend(path, move |path| path.to_str().unwrap())
}
and this spelling appears to work, but note that it's very easy when trying to do something like this to encounter confusing higher-ranked lifetime errors, because closures aren't great at for<'a>
use cases (implicitly introduced here by lifetime elision).
(For example: it's possible to define Lend
without the O
type parameter or the F: Fn
bound, which I did originally since leaving the struct minimally bound is typically preferable. However, while the Deref
impl still works, attempting to construct one a) requires an extra type annotation on the closure parameter and b) causes a (higher-ranked) lifetime error, potentially due to the presence of the type annotation, unlike when the bound is present structurally. It's very rarely clear what the compiler wants in these cases, and it takes some educated guessing to get a shape the compiler is happier about.)
The maximally general version of this kind of self-borrowing pattern is yoke, but it's extremely overkill unless you're in the very specific case that it was designed to support (zero-copy deserialization of complex types). You know if you need it (and will probably end up wanting it before you actually need it).