Generics, Traits vs Enum for pagination

So I have been looking to implement an API wrapper for reddit and was thinking about a design question.

So to give a brief overview Reddit has 6 different types of Things : Comment, Account, Link, Message, Subreddit, Award.

It implements Pagination using something called a Listing which has the common params after, before, limit, count, show

A Listing is basically a pagination container over the 6 different types of Things

I have two options when it comes to implementing this:

I can create a struct for each of the 6 different Things and create a Thing enum with 6 variants wrapping the structs. My listing will then basically be a concrete struct contain a Vec and implement before, after, etc. as methods.

pub enum Thing {
    Comment(Comment),
    Account(User),
    Link(Link),
    Message(Message),
    Subreddit(Subreddit),
    Award(Award),
}

pub struct Listing {
    /// The actual things.
    pub children: Vec<Thing>,
    /// The fullname after this listing.
    pub after: Option<Fullname>,
    /// The fullname before this listing.
    pub before: Option<Fullname>,
    /// The maximum number of items to return in this slice of the listing.
    pub limit: Option<u64>,
    /// The number of items already seen in this listing.
    pub count: Option<u64>,
    /// Optional parameter to override what is shown.
    pub show: Option<String>,
}

The second option is to create a generic Listing<T: Thing> where Thing is a dummy trait which each of those 6 different Things will implement just so that Listing is restricted to those types, or I could just make it unrestricted, whichever works.

My question is what are the pros and cons of each approach? The main con of the first approach I can think of is the code might not look very good and be littered with match statements and that's about it.

Any opinions on this?

I would go with the second option, using a Listing<T>. That way you can write a generic read_paginated() function like this pseudo-code:

async fn read_paginated<T>(endpoint: &str) -> Result<Vec<T>, Error> 
where
  T: DeserializeOwned
{
  let mut items = Vec::new();
  let mut endpoint = format!("http://example.com/{}", endpoint);

  loop {
    let response = reqwest::get(&endpoint).await?;
    let page: Listing<T> = response.json().await?;
    items.extend(page.children);

    match page.next {
      Some(url) => endpoint = url,
      None => return Ok(items),
    }
  }
}
1 Like

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.