AL+

Language: RO (română) EN (English)

Email us: paul@alplustech.com

Go recursive types need a completion rule

Go's type checker can retain incomplete references when representation is unnecessary. Separate recursive graph links from size-dependent array definitions.

Citiți articolul în română

A cycle in a type definition is not automatically an error. The useful question is what information the current operation needs before construction can finish. A reference may be meaningful while the referenced type is incomplete. An operation that needs the missing representation cannot simply proceed on that promise. Go's explanation of type construction offers a precise way to reason about that difference, without turning every recursive definition into a suspected defect.

Mark Freeman's 24 March 2026 article describes implementation improvements in Go 1.26. The release itself was announced on 10 February 2026. March 24 was the date of a technical explanation rather than a new release. The author also says most users should see no observable change. The value for application developers is a reasoning tool, not a requirement to redesign working code.

A reference can precede completion

A type checker builds internal representations while examining declarations. Seeing a name and knowing everything about its underlying representation are different states. The article shows that a pointer can refer to a type still under construction. The checker can retain that reference and complete the related types later. It does not need to expand the entire referenced structure just to represent the pointer relationship.

This explains why recursive graph nodes are a useful mental model. A node can refer to another node, including one of the same type, through a pointer. The relationship describes how values may connect. It does not require the definition to contain an endlessly expanded copy of itself. Keep that distinction between a reference and embedded representation when reading an error about recursion.

The lesson is about dependencies on information. A construction step can move forward if it has enough information for its present responsibility. Later checks can still reject an invalid definition. Deferring a check does not mean abandoning it, and an incomplete reference does not establish that every operation on the referenced type is already valid. The boundary lies in what the operation must inspect.

Array length can demand the missing fact

The article contrasts those references with an array whose length depends on the size of the type being defined. To determine that array's representation, the checker would need information that depends on completing the same type. The dependency cannot be resolved by retaining a reference alone. Completion is waiting on itself for a fact needed to complete it.

For source-level context, the official article uses unsafe.Sizeof to explain this distinction. Measuring a pointer provides different information from inspecting the incomplete defined type itself. Its examples should be read with their precise operations intact. The mere presence of a familiar function name does not establish validity, and changing what the function consumes can change the dependency that matters.

There is no need to reproduce a complicated declaration to use the principle. When reviewing a recursive definition, identify whether the next step needs only a reference or must inspect the missing underlying representation. Then follow the dependency back to the definition being constructed. This approach explains why some cycles are valid and others cannot be completed. It avoids the inaccurate shortcut that every cycle is forbidden.

Use diagnostics before internals

The strongest counterargument is practical: most application developers need clear diagnostics, not a detailed model of type-checker construction. That is right. A compiler error that identifies the problematic declaration may provide enough information to repair the program. Studying the internal implementation should serve a specific question rather than become a new prerequisite for ordinary Go development.

Start with the reported dependency and the intended data model. If the model is a linked structure, establish whether the declaration actually expresses a reference relationship. If the definition requires a size, establish which facts determine that size. These are questions to ask of the source and the diagnostic, not a claim that changing every recursive field to a pointer is the correct repair.

A repair also needs to preserve the program's meaning. Adding indirection changes how values are represented and used. It should follow from the intended model, rather than become a mechanical way to remove an error. This article supplies no local compiler experiment and makes no performance claim about one representation over another. The compiler explanation supports the distinction; project-specific behavior requires its own evidence.

Carry the invariant into design reviews

The broader design insight is useful when a system constructs objects that refer to each other. Distinguish a reference that can be recorded now from an operation that requires completed information. This is an interpretation of the compiler example, not proof that Go's implementation can be transplanted into another system. Different systems have different validity rules and different moments when an unresolved reference becomes unacceptable.

A review can make the dependency explicit by naming the missing fact, the operation that needs it and the point at which it becomes available. If the same operation must finish before its own prerequisite exists, there is a real design problem to explain. If a reference can remain unresolved until a later check, document that check and its responsibility. Incompleteness needs a completion rule, not automatic rejection or unlimited tolerance.

For teams whose application model has become difficult to explain, AL+ offers custom software development. A useful starting point is the smallest relationship whose validity is unclear and the behavior it must support. Go's example encourages that precision: identify which operation needs representation, allow references where they suffice, and keep the eventual validity check visible. Most projects can apply the question without learning every detail of the checker.