Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for calling methods that take self and return the Heads #104

Open
erikjohnston opened this issue Dec 5, 2023 · 2 comments
Open

Comments

@erikjohnston
Copy link

I have a situation where I would like to call a method that takes self on a tail field, and then get the head fields back. I don't think this is possible currently, but would there be interest in adding such an ability? (Or a reason why it might be unsafe?).

An example situation:

struct A;
struct B<'a> {
    a: &'a A
}

impl<'a> B<'a> {
   fn new(&'a A) -> B<'a> { B { a } }

   fn final(self) { ... }
}

#[self_referencing]
struct Wrapper {
    a: A,
    #[borrows(mut a)]
    b: B<'a>,
}

In this case I would like to be able to, given a Wrapper, call B::final and get back a.

I think a possible function signature for the wrapper type would be:

fn into_heads_with_b<R, F>(self, func: F) -> (R, Heads) 
where F: Fn(B<'a>) -> R;

or something?

Thoughts very much welcome (and thanks for this library!) 🙂

@erikjohnston
Copy link
Author

#105 is a rough potential implementation.

@A-Manning
Copy link

The signature of destruct_into_heads can be more general:

fn destruct_into_heads<R>(
    self,
    user: impl for<'this> FnOnce(&'this NonTailFields, OwnedTailFields<'this> ) -> R
) -> (R, Heads);

If we have OwnedTailFields, perhaps it would be possible to add something like

fn rebuild<Params...>(
    self,
    user: impl for<'this> FnOnce(
        &'this NonTailFields<...>,
        OwnedTailFields<'this, ...>
    ) -> OwnedTailFields<'this, Params...>
) -> MyStruct<Params...>

This would make it possible to implement a function like map on a generic struct (trivially, as rebuild is effectively a map over tail fields):

struct Owner;

// Trait to express that a type is parametric over lifetimes
trait ForLt {
    type Of<'lt>;
}

#[self_referencing]
struct MyStruct<T> where T: ForLt {
    owner: Owner,
    
    #[borrows(owner)]
    #[not_covariant]
    tail_field: T::Of<'this>
}

// Trait to express that a function is parametric over lifetimes, and that the input and output types are parametrized by the same lifetime
trait FnOnceLt<Input> where Input: ForLt {
    type Output: ForLt;
    
    fn call_once<'a>(self, Input::Of<'a>) -> <Self::Output as ForLt>::Of<'a>;
}

// Input type for the user function supplied to map
struct MapUserInput<T>(PhantomData<T>);

impl<T> ForLt for MapUserInput<T> where T: ForLt {
    type Of<'a> = (&'a Owner, T::Of<'a>);
}

impl<T> MyStruct<T> where T: ForLt {
    fn map<U, F>(self, f: F) -> MyStruct<U> where
        U: ForLt,
        F: FnOnceLt<MapUserInput<T>, Output = U>, {
     
        self.rebuild(|non_tail_fields, owned_tail_fields| f((non_tail_fields, owned_tail_fields)))
    
    }
}

A fallible version of rebuild would be useful too -

fn try_rebuild<Params..., Error>(
    self,
    user: impl for<'this> FnOnce(
        &'this NonTailFields<...>,
        OwnedTailFields<'this, ...>
    ) -> Result<OwnedTailFields<'this, Params...>, Error>
) -> Result<MyStruct<Params...>, (Heads, Error)>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants