A parsed version should be able to render itself back to its canonical string. Today you write that String method and prove it round-trips, which closes the parsing chapter with something you can see.
Reconstruct the canonical version string from a parsed Version.
Now that a version is a structured value, it should be able to turn back into a
string - the inverse of Parse. The canonical form is fixed: the three numbers
joined by dots, then a - and the dot-joined prerelease identifiers only if a
prerelease is present, then a + and the dot-joined build identifiers only
if build metadata is present. Omit the delimiter entirely when its part is
empty, so 1.2.3 prints as 1.2.3, not 1.2.3-+.
This gives the whole chapter a satisfying close: because both sides use the same canonical grammar, parsing a valid version and printing it back reproduces the original string exactly. That round-trip is a genuine correctness check - if the printed form differs from the input, something in the parse dropped or reordered a part. It is also the first piece of the library that produces visible output, and you will lean on it later when a range prints the comparators it expands into.
// Always "MAJOR.MINOR.PATCH". Append "-" + join(Prerelease, ".") only if there IS one.// Append "+" + join(Build, ".") only if there IS build metadata.func (v Version) String() string { /* ... */ }