Struct update syntax in a typestate scenario

I'm doing an exercise to practice using typestates. I have a Post:

#[derive(Clone)]
struct Post<S> {
    id: post::Id,
    user_id: user::Id,
    title: post::Title,
    body: post::Body,
    state: S,
}

where S is a state:

struct PostDeleted;
struct PostPublished;

I want to create a type transition from Post<PostPublished> to Post<PostDeleted> by using .delete():

impl Post<PostPublished> {
    fn delete(self) -> Post<PostDeleted> {
        Post {
            state: PostDeleted,
            ..self
        }
    }
}

However, the compiler doesn't allow me to use the ..self syntax because Post<PostPublished> is not the same type as Post<PostDeleted>. Fair, considering that typestates were specifically created to combat conversions like these, but what is the nicest way to achieve what I want without doing something like:

impl Post<PostPublished> {
    fn delete(self) -> Post<PostDeleted> {
        Post {
            state: PostDeleted,
            id: self.id,
            user_id: self.user_id,
            title: self.title,
            body: self.body
        }
    }
}

You'll have to do it at least once. For code duplication, you could create e. g. something like a map-style helper function. Of the problem is that you have too many similar types like this, you could even generate it from a macro, I suppose.

Besides the implementation style you showed, one can also write this with a few less usages of "self." using something like

impl Post<PostPublished> {
    fn delete(self) -> Post<PostDeleted> {
        let Post { id, user_id, title, body, .. } = self;
        Post {
            state: PostDeleted,
            id, 
            user_id, 
            title, 
            body,
        }
    }
}
2 Likes

Eventually the plan is to make struct update syntax compatible with changing the type of its generic parameters. Here's the tracking issue for that feature if you're curious

1 Like

You could extract the fields which don't depend on S into a different struct, making it easy to move them as a single group.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.