fat struct style guide?
Julian Fondren
julian.fondren at gmail.com
Tue Feb 24 19:48:07 UTC 2026
On Tuesday, 24 February 2026 at 16:56:57 UTC, monkyyy wrote:
> Its c-like code and enabling "unsafe" void* hacks.
>
> Gist code from last night(I already see one improvement and
> expect more):
>
> ```d
> #!opend test app
> import std;
> import core.stdc.stdlib;
> import core.sys.posix.dlfcn;
>
> enum animalid{cow=7,chicken,horse};
>
> struct animal{
> animalid id;
> string say;
> int age;
> }
> ...
> ```
An algebraic datatype, a sumtype, a discriminated union, etc. And
a 'fat pointer' is a just pointer to tagged data with some
Data-Oriented Design implications about the data. Wikipedia has
one good line about it:
> A tagged union can be seen as the simplest kind of
> self-describing data format. The tag of the tagged union can be
> seen as the simplest kind of metadata.
With std.sumtype a 1:1 version of your code is a real pain, but
your method would be more painful if the variants weren't so
similar. std.sumtype's also less painful over time, refusing
newanimal("idk"), having a compile-time exhaustiveness check.
There are some rough edges like .each! not working here.
Skipping "do",
```d
import std;
import std.sumtype;
enum animalid {cow = 7, chicken, horse};
struct Cow { string say = "moo"; int age; }
struct Chicken { string say = "cluck"; int age; }
struct Horse { string say = "niah"; int age; }
alias Animal = SumType!(Cow, Chicken, Horse);
animalid id(Animal a) => a.match!(
(Cow _) => animalid.cow,
(Chicken _) => animalid.chicken,
(Horse _) => animalid.horse,
);
int age(Animal a) => a.match!(
(Cow a) => a.age,
(Chicken a) => a.age,
(Horse a) => a.age,
);
void speak(Animal a) => a.match!(
(Cow a) => writeln("this cow says ", a.say),
(Chicken a) => writeln("this chicken says ", a.say),
(Horse a) => writeln("this horse says ", a.say),
);
int[animalid] maxage;
bool isdead(Animal a) => a.age > maxage[a.id];
void passtime(ref Animal a, int years) {
a.match!(
(ref Cow a) => a.age += years,
(ref Chicken a) => a.age += years,
(ref Horse a) => a.age += years,
);
}
unittest {
Animal[] foo;
foo ~= Cow().Animal;
foo ~= Chicken().Animal;
foo ~= Horse().Animal;
writeln(foo);
foreach (ref a; foo) a.passtime(10);
maxage[animalid.chicken] = 5;
maxage[animalid.cow] = 15;
maxage[animalid.horse] = 10;
foo.map!isdead.writeln;
maxage[animalid.chicken] = 500;
foo.map!isdead.writeln;
}
```
std.sumtype's more similar to ML or Rust types. Ada and Nim feel
a lot more like your method:
```nim
import std/[strformat, sequtils]
type
AnimalKind = enum
cow, chicken, horse
Animal = object
kind: AnimalKind
say: string
age: int = 0
# I typo'd "chicken" in this function and got confused when
nothing died.
func newanimal(s: string): Animal =
case s
of "cow": Animal(kind: cow, say: "moo")
of "chicken": Animal(kind: chicken, say: "cluck")
else: Animal(kind: horse, say: "niah")
proc speak(a: Animal) = echo &"This {a.kind} says {a.say}"
var maxage: array[AnimalKind, int]
proc old(a: var Animal) = a.age += 10
proc isdead(a: Animal): bool = a.age > maxage[a.kind]
when defined(unittest):
var foo: seq[Animal]
foo.add newanimal("chicken")
foo.add newanimal("cow")
foo.add newanimal("idk")
echo foo
for a in foo: a.speak
for a in foo.mitems: a.old
maxage[chicken] = 5
maxage[cow] = 15
maxage[horse] = 10
echo foo.mapIt(it.isdead)
maxage[chicken] = 500
echo foo.mapIt(it.isdead)
```
More information about the Digitalmars-d-learn
mailing list