> While C# does have AOT capabilities nowadays this is not as mature as Go's and not all platforms support it
https://learn.microsoft.com/en-us/dotnet/core/deploying/nati...
Only Android is missing from that list (marked as "Experimental"). We could argue about maturity but this is a bit subjective.
> Go also has somewhat better control over data layout
How? C# supports structs, ref structs (stack allocated only structures), explicit stack allocation (`stackalloc`), explicit struct field layouts through annotations, control over method local variable initialization, control over inlining, etc. Hell, C# even supports a somewhat limited version of borrow checking through the `scoped` keyword.
> This is meant to be something of a 1:1 port rather than a rewrite, and the old code uses plain functions and data structures without an OOP style.
C# has been consistently moving into that direction by taking more and more inspiration from F#.
The only reasonable reason would be extensive usage of structural typing which is present in TS and Go but not in C#.
That's sort of the problem with C#. It couples the type (struct vs class) with allocation. C# started life by copying 1990's Java "everything-is-a-reference". So it's in a weird place where things were bolted on later to give more control but still needs to support the all-objects-are-refs style. C# is just not ergonomic if you need to care about data layout in memory.
Go uses a C-like model. Everything is a value type. Real pointers are in the language. Now you can write a function that inputs pointers and does not care whether they point to stack, heap, or static area. That function can be used for all 3 types, no fuss.
Agree. Where things are allocated is a consumer decision.
> C# is just not ergonomic if you need to care about data layout in memory
I disagree. I work on a public high performance C# code and I don't usually face issues when dealing with memory allocations and data layout. You can perfectly use structs everywhere (value types) and pass references when needed (`ref`).
> Now you can write a function that inputs pointers and does not care whether they point to stack, heap, or static area.
You can do this perfectly fine in C#, it might not be what some folks consider "idiomatic OOP" but I could not care less about them.