Skip to content

Cell redesign proposal #58

Open
Open
@hcpl

Description

@hcpl

This is a proposal to remake Cell and related functionality to simplify fixing issues #45, #46 and possibly #47 (since this proposal, if accepted, will allow defining custom behaviour of cell elements in case of the outer table being shrinked).

Here are some ways how this proposal can be implemented.

1. Statically dispatched trait-based

trait CellContent { ... }
impl CellContent for Vec<String> { ... }    // represents lines
impl CellContent for Table { ... }
...
struct Cell<T: CellContent> {
    content: T,
    ...
}
  • Pros:

    • Easy for implementation: you don't have to care about the possibility of having values of different types in any given moment of time.
    • Safe: the table will behave exactly like you (author) designed it to be, since everything down to cells is verified by you.
    • Semi-easy to adapt existing public API:
      • Serious changes are likely to be required only for Cells, for other structs they're either avoidable, or have to do something with formatting and styling.
      • Thanks to this StackOverflow answer external appearance of cell! macros can remain the same.
  • Cons:

    • Unusable if you need differently typed contents, since the cell itself is locked to one type and because of the reason below. (Well, unusable if you don't want to convert everything to String. If this is the case this mode works perfectly fine)
    • Semi-difficult to adapt existing public API:
      • Since Table's and Row's backing storage is a homogeneous Vec, the <T> parameter would be needed to spread across the whole API.

2. Dynamically dispatched trait-based

For trait definition see above

struct Cell {
    content: Box<CellContent>,    // could be other box structure here
    ...
}
  • Pros:

    • Flexible: users will be able to define new types with their own behaviours in the table.

    • Easy to adapt existing public API:

      • Cell constructors: types can be backwards-compatibly generalized, whereas the amount of constructors remains the same.
      • This means cell! macro wouldn't have to be modified at all.
      • And (probably) everything else wouldn't change neither.

      As a result, public API changes would be minimal and backwards-compatible.

  • Cons:

    • Ineffective: Cell has to store CellContent boxed since we wouldn't know the exact type. This would certainly have performance implications, though their extent varies among usages.
    • Mildly unsafe: there should be some precautionary measures against bad CellContent implementations.

3. Enum-based

enum CellContentBox {
    Text(Vec<String>),    // represents lines
    Table(Table),
    ...
}

impl CellContentBox { ... }
struct Cell {
    content: CellContentBox,
    ...
}
  • Pros:

    • Effective: theoretically, everything in this case can be stored on stack.
    • Safe: same note for Statically dispatched trait-based.
  • Cons:

    • Inflexible: there is no easy way to add new types. You have to manually add a variant to CellContentBox and add implementation into all related functions and methods, which is a more cumbersome process than implementing a trait. It also makes code less clean and readable.
    • Difficult to adapt existing public API:
      • Cell constructors would probably have constructors for every enum variant.
      • Macros aren't capable of detecting types of input values, so this forces having a dedicated cell_*! for every enum variant.

UPD: More info about macros and wording.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions