Skip to content

Design questions #1

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

Open
kriomant opened this issue Sep 8, 2023 · 3 comments
Open

Design questions #1

kriomant opened this issue Sep 8, 2023 · 3 comments

Comments

@kriomant
Copy link

kriomant commented Sep 8, 2023

I'm trying to migrate from another library to this one (because current one is unmaintained and doesn't support fresh features).
I have several questions regarding design choices:

Why are fields private?
Since types are generated they don't try to maintain invariants (like 'photo field should have caption' or something like this), so why bother and wrap everything into getter/setter methods?
Currently my code sometimes modify messages or entities and I have hard time porting this.

Why are Cows everywhere?
Ok, it may have some sense for requests (but it doesn't worth added complexity, imho), but why do it for response types, where all fields are read from network and thus are always owned (do I miss anything)?

@fmeef
Copy link
Owner

fmeef commented Sep 10, 2023

Ok so this is a bit complicated. The reason for all this weirdness has to do with fundamental incompatibilities between rust's type system and telegram's own json types. Due to the way rust handles generics rust types can't have circular references, i.e. contain themselves. Unfortunately, telegram types can have these cycles, so automatically generating rust types in a naive way breaks.

To solve this, the easy way is to break cycles with a Box. This ends up being very expensive in terms of allocations if every field in every struct was all Box. The most runtime-efficient solution is to minimize the Boxen, boxing the fewest number of types in order to remove all cycles. This ends up being equivalent to minimum feedback arc set (https://en.wikipedia.org/wiki/Feedback_arc_set) which is NP hard so it must be approximated. The approximation doesn't have any guarantees of which particular fields are boxed, so exposing the fields as public would essentially be an unstable api.

My solution to this was to only allow access via either Cows or Deref, since that can be created from a Box. I expose them both to allow ergonomics and better support for lifetimes (cows often have issues with complex lifetimes in trait bounds). If ownership is required a field can be cloned which is often inexpensive.

If you or anyone has a better way of doing this I am definitely open to hearing it.

@MrFoxPro
Copy link

MrFoxPro commented Feb 2, 2025

I wrote my own types generator and I can say if you're not bothering with &'str and use owned types (mostly String) everywhere, there are only two places where you need Box: MaybeInaccessibleMessage and GiveawayCompleted.giveaway_message.
I feel like instancing tiny structs with owned types inside doesn't really hurts performance.
Especially if you're dealing with dynamic text formatting, you end up with heap allocations anyway.
Or am I wrong?

@fmeef
Copy link
Owner

fmeef commented Mar 15, 2025

Types that need to be boxed when owned include User and Message (as seen in Update type's children)There are cycles in the telegram API spec for types represented by json objects.

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

3 participants