Inspired by @withoutboat 's post, especially the “Offset-based solutions” sections, I come up with an idea as the following:
use std::fmt::Display;
// In std or core
trait Access<'a,Src>: Copy {
type Tgt: 'a;
fn access<'b>(&self, src: &'b Src) -> Self::Tgt;
}
impl<'a, Src, Tgt, F> Access<'a,Src> for F
where F: (FnOnce(&Src) -> Tgt) + Copy,
Tgt: 'a
{
type Tgt = Tgt;
fn access<'b>(&self, src: &'b Src) -> Tgt {
(*self)(src)
}
}
//user code
//sugar signature as
//fn print<S,T>(a:impl S::as T, s:&S) where T: ?Sized + Display
fn print<'a,S,T>(a:impl Access<'a,S,Tgt=&'a T>, s:&S)
where T: ?Sized + Display + 'a
{
println!("{}", a.access(s))
}
fn main() {
let v = (1,"Hello, World!",true);
//sugar as:
//print(_::1,&v);
print(|v:&(_,&'static str,_)| v.1,&v);
struct MyStruct {
field: &'static str
};
let v = MyStruct{ field: "Good bye, World!" };
//sugar as:
//print(_::field,&v);
print(|v:&MyStruct| v.field,&v);
}
You can run the above in playground.
This is similar to C++'s member offset feature.
Things to clarify:
-
@withoutboats didn’t prefer this compare to Pin, I have no objection on this.
-
This is NOT an alternative to the proposed Pin type. In my opinion Pin type (which cannot move any more) serves different purposes than the above “Access in need” and have different use cases, although they can both be used to create self referencing data.
-
I am currently using similar thing in my interpreter - to present the self-referencing code (you will need recursion/loops, right?) interpreting.
-
Pin seems requires some language support and so need to wait for stabilization. My proposal is pure desugaring (and maybe improvement on type inference) and didn’t really add anything new. Even without the desugaring, adding Access (maybe AccessMut variant as well) to std is an improvement I think, because in my use cases I have custom instances of Access to something inside Vecs or HashMaps etc.
Now I am not sure the above should convince you that we should have this thing RFCed?