{"id":13713150,"url":"https://github.com/romshark/Go-1-2-Proposal---Immutability","last_synced_at":"2025-05-06T23:30:41.172Z","repository":{"id":97896600,"uuid":"147207476","full_name":"romshark/Go-1-2-Proposal---Immutability","owner":"romshark","description":"A a Go 1/2 language feature proposal to immutability","archived":true,"fork":false,"pushed_at":"2020-01-19T14:22:40.000Z","size":725,"stargazers_count":171,"open_issues_count":6,"forks_count":4,"subscribers_count":15,"default_branch":"master","last_synced_at":"2024-11-13T23:33:13.412Z","etag":null,"topics":["go","golang","language","proposal"],"latest_commit_sha":null,"homepage":"https://github.com/golang/go/issues/27975","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/romshark.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-09-03T13:17:22.000Z","updated_at":"2024-03-10T08:15:33.000Z","dependencies_parsed_at":"2023-05-31T14:30:19.386Z","dependency_job_id":null,"html_url":"https://github.com/romshark/Go-1-2-Proposal---Immutability","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/romshark%2FGo-1-2-Proposal---Immutability","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/romshark%2FGo-1-2-Proposal---Immutability/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/romshark%2FGo-1-2-Proposal---Immutability/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/romshark%2FGo-1-2-Proposal---Immutability/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/romshark","download_url":"https://codeload.github.com/romshark/Go-1-2-Proposal---Immutability/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252787171,"owners_count":21804211,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["go","golang","language","proposal"],"created_at":"2024-08-02T23:01:28.556Z","updated_at":"2025-05-06T23:30:39.996Z","avatar_url":"https://github.com/romshark.png","language":null,"funding_links":[],"categories":["Repositories"],"sub_categories":[],"readme":"![Immutability - A Go Language Feature Proposal](https://github.com/romshark/Go-2-Proposal---Immutability/blob/master/document_header.png \"Immutability - A Go Language Feature Proposal\")\n\n# Go - Immutability\nThis document describes a language feature proposal to immutability for the [Go\nprogramming language](https://golang.org). The proposed feature targets the\ncurrent [Go 1.x (\u003e 1.11) language specification](https://golang.org/ref/spec)\nand doesn't violate [the Go 1 compatibility\npromise](https://golang.org/doc/go1compat). It also describes [an even better\napproach to immutability](#3-immutability-by-default-go--2x) for a hypothetical, backward-incompatible [Go 2\nlanguage specification](https://blog.golang.org/toward-go2).\n\n\u003ctable\u003e\n\t\u003ctr\u003e\n\t\t\u003ctd\u003e\u003cb\u003eAuthor\u003c/b\u003e\u003c/td\u003e\n\t\t\u003ctd\u003e\n\t\t\t\u003ca href=\"https://github.com/romshark\"\u003eRoman Sharkov\u003c/a\u003e (\u003ca href=\"mailto:roman.scharkov@gmail.com\"\u003eroman.scharkov@gmail.com\u003c/a\u003e)\n\t\t\u003c/td\u003e\n\t\u003c/tr\u003e\n\t\u003ctr\u003e\n\t\t\u003ctd\u003e\u003cb\u003eStatus\u003c/b\u003e\u003c/td\u003e\n\t\t\u003ctd\u003e\u003ca href=\"https://github.com/golang/go/issues/27975\"\u003ePublic (#27975)\u003c/a\u003e\u003c/td\u003e\n\t\u003c/tr\u003e\n\t\u003ctr\u003e\n\t\t\u003ctd\u003e\u003cb\u003eVersion\u003c/b\u003e\u003c/td\u003e\n\t\t\u003ctd\u003e1.0.1\u003c/td\u003e\n\t\u003c/tr\u003e\n\u003c/table\u003e\n\n**Table of Contents**\n- [Go - Immutability](#go---immutability)\n\t- [1. Introduction](#1-introduction)\n\t\t- [1.1. Current Problems](#11-current-problems)\n\t\t\t- [1.1.1. Ambiguous Code and Dangerous Bugs](#111-ambiguous-code-and-dangerous-bugs)\n\t\t\t- [1.1.2. Vague and Bloated Documentation](#112-vague-and-bloated-documentation)\n\t\t\t- [1.1.3. The - Slow but Safe vs Dangerous but Fast - Dilemma](#113-the---slow-but-safe-vs-dangerous-but-fast---dilemma)\n\t\t\t- [1.1.4. Inconsistent Concept of Constants](#114-inconsistent-concept-of-constants)\n\t\t- [1.2. Benefits](#12-benefits)\n\t\t\t- [1.2.1. Safe and Precise Code](#121-safe-and-precise-code)\n\t\t\t- [1.2.2. Self-Explaining Code](#122-self-explaining-code)\n\t\t\t- [1.2.3. Increased Runtime Performance](#123-increased-runtime-performance)\n\t- [2. Proposed Language Changes](#2-proposed-language-changes)\n\t\t- [2.1. Immutable Fields](#21-immutable-fields)\n\t\t- [2.2. Immutable Methods](#22-immutable-methods)\n\t\t- [2.3. Immutable Arguments](#23-immutable-arguments)\n\t\t- [2.4. Immutable Return Values](#24-immutable-return-values)\n\t\t- [2.5. Immutable Variables](#25-immutable-variables)\n\t\t- [2.6. Immutable Interface Methods](#26-immutable-interface-methods)\n\t\t- [2.7. Slice Aliasing](#27-slice-aliasing)\n\t\t- [2.8. Address Operators](#28-address-operators)\n\t\t\t- [2.8.1. Taking the address of a variable](#281-taking-the-address-of-a-variable)\n\t\t\t- [2.8.2. Dereferencing a pointer](#282-dereferencing-a-pointer)\n\t\t- [2.9. Immutable Reference Types](#29-immutable-reference-types)\n\t\t\t- [2.9.1. Pointer Examples](#291-pointer-examples)\n\t\t\t\t- [2.9.1.1. Immutable pointer to a mutable object](#2911-immutable-pointer-to-a-mutable-object)\n\t\t\t\t- [2.9.1.2. Mutable pointer to an immutable object](#2912-mutable-pointer-to-an-immutable-object)\n\t\t\t\t- [2.9.1.3. Immutable pointer to an immutable object](#2913-immutable-pointer-to-an-immutable-object)\n\t\t\t- [2.9.2. Slice Examples](#292-slice-examples)\n\t\t\t\t- [2.9.2.1. Immutable slice of immutable objects](#2921-immutable-slice-of-immutable-objects)\n\t\t\t\t- [2.9.2.2. Mutable slice of immutable objects](#2922-mutable-slice-of-immutable-objects)\n\t\t\t\t- [2.9.2.3. Immutable slice of mutable objects](#2923-immutable-slice-of-mutable-objects)\n\t\t\t\t- [2.9.2.4. Mutable slice of mutable objects](#2924-mutable-slice-of-mutable-objects)\n\t\t\t- [2.9.3. Map Examples](#293-map-examples)\n\t\t\t\t- [2.9.3.1. Mutable map of immutable keys to mutable objects](#2931-mutable-map-of-immutable-keys-to-mutable-objects)\n\t\t\t\t- [2.9.3.2. Mutable map of mutable keys to immutable objects](#2932-mutable-map-of-mutable-keys-to-immutable-objects)\n\t\t\t\t- [2.9.3.3. Mutable map of immutable keys to immutable objects](#2933-mutable-map-of-immutable-keys-to-immutable-objects)\n\t\t\t\t- [2.9.3.4. Immutable map of immutable keys to immutable objects](#2934-immutable-map-of-immutable-keys-to-immutable-objects)\n\t\t\t- [2.9.4. Channel Examples](#294-channel-examples)\n\t\t\t\t- [2.9.4.1. Immutable channels of immutable objects](#2941-immutable-channels-of-immutable-objects)\n\t\t\t\t- [2.9.4.2. Immutable channels of mutable objects](#2942-immutable-channels-of-mutable-objects)\n\t\t\t\t- [2.9.4.3. Mutable channels of immutable objects](#2943-mutable-channels-of-immutable-objects)\n\t\t- [2.10. Immutable Package-Scope Variables](#210-immutable-package-scope-variables)\n\t\t- [2.11. Explicit Type Casting](#211-explicit-type-casting)\n\t\t\t- [2.11.1. Simple Cast](#2111-simple-cast)\n\t\t\t- [2.11.2. Literal Type Casting](#2112-literal-type-casting)\n\t\t\t- [2.11.3. Prohibition of Casting Immutable- to Mutable Types](#2113-prohibition-of-casting-immutable--to-mutable-types)\n\t\t- [2.12. Implicit Casting](#212-implicit-casting)\n\t\t\t- [2.12.1. Implicit Casting of Pointer Receivers](#2121-implicit-casting-of-pointer-receivers)\n\t\t- [2.12. Standard Library](#212-standard-library)\n\t- [3. Immutability by Default (Go \u003e= 2.x)](#3-immutability-by-default-go--2x)\n\t\t- [3.1. Benefits](#31-benefits)\n\t\t\t- [3.1.1. Safety by Default](#311-safety-by-default)\n\t\t\t- [3.1.2. Less Code](#312-less-code)\n\t\t\t- [3.1.3. No `const` Keyword Overloading](#313-no-const-keyword-overloading)\n\t- [4. FAQ](#4-faq)\n\t\t- [4.1. Are the items within immutable slices/maps also immutable?](#41-are-the-items-within-immutable-slicesmaps-also-immutable)\n\t\t- [4.2. Go is all about simplicity, so why make the language more complicated?](#42-go-is-all-about-simplicity-so-why-make-the-language-more-complicated)\n\t\t- [4.3. Aren't other features such as generics and better error handling not more important right now?](#43-arent-other-features-such-as-generics-and-better-error-handling-not-more-important-right-now)\n\t\t- [4.4. Why overload the `const` keyword instead of introducing a new keyword like `immutable` etc.?](#44-why-overload-the-const-keyword-instead-of-introducing-a-new-keyword-like-immutable-etc)\n\t\t- [4.5. How are constants different from immutable types?](#45-how-are-constants-different-from-immutable-types)\n\t\t- [4.6. Why do we need immutable receivers if we already have copy-receivers?](#46-why-do-we-need-immutable-receivers-if-we-already-have-copy-receivers)\n\t\t- [4.7. Why do we need immutable interface types?](#47-why-do-we-need-immutable-interface-types)\n\t\t- [4.8. Doesn't the `const` qualifier add boilerplate and make code harder to read?](#48-doesnt-the-const-qualifier-add-boilerplate-and-make-code-harder-to-read)\n\t\t- [4.9. Why do we need the distinction between immutable and mutable reference types?](#49-why-do-we-need-the-distinction-between-immutable-and-mutable-reference-types)\n\t\t- [4.10. Why implicitly cast mutable to immutable types?](#410-why-implicitly-cast-mutable-to-immutable-types)\n\t\t- [4.11. Can't these problems be solved by a linter?](#411-cant-these-problems-be-solved-by-a-linter)\n\t- [5. Other Proposals](#5-other-proposals)\n\t\t- [5.1. proposal: spec: add read-only slices and maps as function arguments #20443](#51-proposal-spec-add-read-only-slices-and-maps-as-function-arguments-20443)\n\t\t\t- [5.1.1. Disadvantages](#511-disadvantages)\n\t\t\t- [5.1.2. Similarities](#512-similarities)\n\t\t- [5.2. proposal: Go 2: read-only types #22876](#52-proposal-go-2-read-only-types-22876)\n\t\t\t- [5.2.1. Disadvantages](#521-disadvantages)\n\t\t\t- [5.2.2. Differences](#522-differences)\n\t\t\t- [5.2.3. Similarities](#523-similarities)\n\n## 1. Introduction\nImmutability is a technique used to prevent mutable shared state, which is a\nvery common source of bugs, especially in concurrent environments, and can be\nachieved through the concept of **immutable types**.\n\nBugs caused by mutable shared state are not only hard to find and fix, but\nthey're also hard to even identify. Such kind of problems can be avoided by\nsystematically limiting the mutability of certain objects in the code. But a Go\n1.x developer's current approach to immutability is manual copying, which lowers\nruntime performance, code readability, and safety. Copying-based immutability\nmakes code verbose, imprecise and ambiguous because the intentions of the code\nauthor are never clear.\n\n**Immutable types** can help achieve this goal more elegantly improving the\nsafety, readability, and expressiveness of the code. They're based on 5\nfundamental rules:\n\n- **I.** Each and every type has an immutable counterpart.\n- **II.** Assignments to objects of an immutable type are illegal.\n- **III.** Calls to mutating methods (methods with a mutable receiver type) on\n  objects of an immutable type are illegal.\n- **IV.** Mutable types can be cast to their immutable counterparts, but not the\n  other way around.\n- **V.** Immutable interface methods must be implemented by a method with an\n  immutable receiver type.\n\nThese rules can be enforced by making the compiler scan all objects of immutable\ntypes for illegal modification attempts, such as assignments and calls to\nmutating methods and fail the compilation. The compiler would also need to\ncheck, whether types correctly implement immutable interface methods.\n\nIdeally, a safe programming language should enforce [immutability by\ndefault](#3-immutability-by-default-go--2x) where all types are immutable unless\nthey're explicitly qualified as mutable because forgetting to make an object\nimmutable is easier, then accidentally making it mutable. But this concept would\nrequire significant, backward-incompatible language changes breaking existing Go\n1.x code. Thus such an approach to immutability would only be possible in a new\nbackward-incompatible Go 2.x language specification.\n\nTo prevent breaking Go 1.x compatibility this document describes a\n**backward-compatible** approach to adding support for immutable types by\noverloading the `const` keyword ([see here for more\ndetails](#44-why-overload-the-const-keyword-instead-of-introducing-a-new-keyword-like-immutable-etc))\nto act as an immutable type qualifier.\n\n### 1.1. Current Problems\n\nThe current approach to immutability (namely copying) has a number of\ndisadvantages listed below and sorted by importance in descending order.\n\n#### 1.1.1. Ambiguous Code and Dangerous Bugs\nThe absence of immutable types can lead to ambiguous code that results\nin dangerous, hard to find bugs. Consider the following method definition:\n\n```go\n// Method ...\nfunc (r *T) Method(\n\ta *T,\n\tb *T,\n\tv []*T,\n) (rv *T) {/*...*/}\n```\nThe above code is ambiguous, it doesn't represent the intentions of its original\nauthor:\n- Will it produce any side-effects on `r`?\n- Will it mutate the `T`s referenced by `a` and `b`?\n- Will it mutate `v`?\n- Will it mutate any `T` referenced by any item of `v`?\n- Is the `T` referenced by `rv` allowed to be mutated?\n\nAll those questions can lead to bugs if they're not properly answered, and\n[documentation never answers them\nreliably](#112-vague-and-bloated-documentation).\n\nIf the above function is exported from a 3-rd party package `xyz` that's\nimported to a project `P` as an external dependency and the documentation\npromises (or \"claims\") to not mutate any of the symbols, the code in `P` will be\nwritten with this assumption in mind.\n\nAt any time the vendor of `xyz` might change its behavior, either intentionally\nor unintentionally, which will introduce bugs and data corruption:\n- `a`, `b`, `v` or any items of `v` might get aliased and mutated either\n  directly (in the scope of `xyz.T.Method`) or indirectly (at an unspecified\n  point in time).\n- New side-effects could be introduced to `r`.\n- `rv` might get aliased and introduce unwanted side-effects when mutated by the\n  `xyz.T.Method` caller.\n\nIn the worst case, the maintainers of `P` won't even be informed about the\nmutations that were unintentionally introduced to a newer version of\n`xyz.T.Method` through a bug in its implementation. But even if the vendors of\n`xyz` correctly update the changelog and the documentation introducing new\nintentional side-effects, chances are high that the maintainers of `P` miss the\nchanges in the documentation and fail to react accordingly. `P` will continue to\ncompile, but its **outputs will become corrupted** which can't always be easily\ndetected even in the presence of extensive automated testing.\n\n#### 1.1.2. Vague and Bloated Documentation\nDocumentation never represents **actual** intentions, it represents **claimed**\nintentions. Claimed intentions can't be relied on, because claims are not\nguaranteed to remain in sync with actual code behavior.\n\nTo avoid ambiguous code developers often describe *mutability recommendations*\nof variables, fields, arguments, methods and return values. Not only does this\nunnecessarily complicate and bloat up the documentation, but it also makes it\nerror-prone and redundant. Documentation can easily get out of sync with the\nactual code because it can't be verified algorithmically.\n\n#### 1.1.3. The - Slow but Safe vs Dangerous but Fast - Dilemma\nCopies are the only way to achieve immutability in Go 1.x, but copies inevitably\ndegrade runtime performance. This dilemma encourages Go 1.x developers to either\nwrite unsafe mutable APIs when targeting optimal runtime performance or safe but\nslow and copy-code bloated ones.\n\nUnfortunatelly, optimal performance and code safety are currently mutually\nexclusive, even though having both would be possible with compiler-enforced\nimmutable types at the cost of a slightly decreased compilation time.\n\n#### 1.1.4. Inconsistent Concept of Constants\nCurrently, Go 1.x won't allow non-scalar constants such as constant slices:\n```\nconst each2 = []byte{'e', 'a', 'c', 'h'} // Compile-time error\n```\n\n[Robert Griesemer](https://github.com/griesemer) stated in his comment to\n[proposal #6386](https://github.com/golang/go/issues/6386) that this is by\nlanguage design, quote:\n\u003e This is neither a defect of the language nor the design. The language was\n_deliberately_ designed to only permit constant of basic types.\n\nBut many developers still claim it to be a major design flaw because exclusive\nimmutability for scalar types only leads to inconsistency in the language\ndesign. What most developers really need is not *constants of arbitrary types*\nbut rather **immutable package-scope variables**, which can be implemented\nconsistently with the help of immutable types:\n```\nvar each2 const []byte = []byte{'e', 'a', 'c', 'h'}\n```\nEven though technically `each2` is not a *constant* but an *immutable\npackage-scope variable* - it solves the mutability problem.\n\n### 1.2. Benefits\nSupport for immutable types would provide the benefits listed below and sorted\nby importance in descending order.\n\n#### 1.2.1. Safe and Precise Code\nImmutable types make APIs less ambiguous.\n\nWith immutable types the situations described in the [previous\nsection](#111-ambiguous-code-and-dangerous-bugs) wouldn't even be possible,\nbecause the author of the function of the external package would need to\nexplicitly qualify immutable types as such to make the compiler enforce the\nguarantee:\n\n```go\n// Method ...\nfunc (r * const T) Method(\n\ta * const T,\n\tb * T,\n\tv const [] * const T,\n) (\n\trv * const T,\n\trv2 * T,\n) {\n\t/*...*/\n}\n```\nThe above code is unambiguous and precise. It clearly represent the intentions\nof its original author and answers all critical questions reliably:\n- Will it produce any side-effects on `r`?\n    - **No, it can't. Its receiver is immutable**\n- Will it mutate the `T` referenced by `a`?\n    - **No, it can't. The `T` referenced by `a` is immutable**\n- Will it mutate `v`?\n\t- **No, it can't. `v` is immutable**\n- Will it mutate any `T` referenced by any item of `v`?\n    - **No, it can't. The `T`s referenced by any item of `v` are immutable**\n- Is the `T` referenced by `rv` allowed to be mutated?\n\t- **No, it's not. The `T` referenced by `rv` is immutable**\n- Will it mutate the `T` referenced by `b`?\n\t- **Yes, it potentially will!**\n- Is the `T` referenced by `rv2` allowed to be mutated?\n\t- **Yes, it won't lead to unwanted side-effects**\n\nWhenever a mutable type is taken, returned or provided it's assumed that its\nstate will potentially be mutated.\n\nThe user of this function would make decisions based on the actual function\ndefinition in the code instead of relying on the potentially inconsistent\ndocumentation.\n\nIf the vendors of this function decide to change the mutability of either an\ninput or output type or the mutability of the object the method operates on -\nthey will have to change the type introducing breaking API changes causing\ncompiler errors and making the user pay attention to whether or not everything's\nright.\n\nThe vendors won't be able to just silently introduce mutations causing bugs! The\ncompiler will prevent this from happening either before the vendors release the\nupdate (assuming that the code is compiled by a CI/CD system before publication)\nor during the user's local build in the worst case.\n\n#### 1.2.2. Self-Explaining Code\nWith immutable types, there's no need to explicitly describe mutability\nrecommendations in the documentation. When immutable types are declared as such\nthen the code becomes self-explaining:\n- An **argument** or a **variable** of an immutable type can be relied on not\n  being changed neither inside the scope it's declared in, nor in the scopes\n  it's passed to.\n- An immutable **method** (or a \"function with a **receiver** of an immutable\n  type\" if you will) - can be relied on not changing the object it operates on.\n- A **return value** of an immutable type can be relied on not being changed by\n  the function caller.\n- A **field** of an immutable type can be relied on not being changed as soon as\n  the object is initialized, even inside the scope of its origin package.\n\n#### 1.2.3. Increased Runtime Performance\nImmutability provides a way to safely avoid unnecessary copying as well as\nunnecessary indirections through mutable and immutable interfaces (because\ninterfaces do have a cost).\n\nImmutability also makes specific compiler optimizations possible. Whether or not\nthose optimization opportunities are exploited later on is rather irrelevant to\nthis particular proposal.\n\n## 2. Proposed Language Changes\nThe language must be adjusted to support the `const` qualifier inside type\ndefinitions to qualify certain types as immutable.\n\nThe compiler must enforce the following rules:\n- Immutable types are declared with the `const` qualifier prepended.\n- Assignments to objects of an immutable type are illegal.\n- Calls to mutating methods (methods with a mutable receiver type) on\n  objects of an immutable type are illegal.\n- Calls to mutating interface methods on immutable interface references\n  are illegal.\n- Immutable types cannot be cast to their mutable counterparts.\n- Types must implement immutable interface methods using an immutable receiver\n  type.\n- Mutable types are implicitly cast to their immutable counterparts.\n- During method calls - pointer receivers must be implicitly cast in both\n  directions (allowing immutable to mutable cast) if the types of the objects\n  they're pointing to are equal.\n\nIt is to be noted, that all proposed changes are fully backward-compatible and\ndon't require any breaking changes to be introduced to the Go 1.x language\nspecification. Code written in previous versions of Go 1.x will continue to\ncompile and work as usual.\n\n### 2.1. Immutable Fields\nStruct fields of an immutable type can only be set during the object\ninitialization and are then immutable for the entire lifetime of the object\nwithin any context.\n\n```go\ntype Object struct {\n\tImmutableField const * const Object // Immutable\n\tMutableField   *Object              // Mutable\n}\n\n// MutatingMethod is a non-const method\nfunc (o *Object) MutatingMethod() {\n\to.MutableField = \u0026Object{}\n\to.ImmutableField = \u0026Object{} // Compile-time error\n}\n\nfunc main() {\n\t// Immutable fields are immutable once the object is initialized\n\tobj := Object{\n\t\tImmutableField: \u0026Object{\n\t\t\tImmutableField: nil,\n\t\t},\n\t}\n\n\tobj.MutableField = \u0026Object{}\n\tobj.ImmutableField = nil                      // Compile-time error\n\tobj.ImmutableField.ImmutableField = \u0026Object{} // Compile-time error\n}\n```\n**Expected compilation errors:**\n```\n.example.go:9:23 cannot assign to immutable field (Object.ImmutableField) of o (type const * const Object)\n.example.go:21:25 cannot assign to immutable field (Object.ImmutableField) of obj (type const * const Object)\n.example.go:22:40 cannot assign to contextually immutable field (Object.ImmutableField) of obj (type const * const Object)\n```\n\n----\n\n### 2.2. Immutable Methods\nImmutable methods are declared using the `const` qualifier on the\nfunction-receiver and guarantee to not mutate the receiver in any way when\ncalled. They can safely be used in immutable contexts, such as within other\nimmutables methods and/or on immutable objects.\n\nTechnically, this feature should rather be called \"immutable function\nreceivers\".\n\n```go\ntype Object struct {\n    mutableField *Object // Mutable\n}\n\n// MutatingMethod is a non-const method.\nfunc (o *Object) MutatingMethod() const * const Object {\n    o.mutableField = \u0026Object{}\n    return o.ImmutableMethod()\n}\n\n// ImmutableMethod is a const method.\n// It's illegal to mutate any fields of the receiver.\n// It's illegal to call mutating methods of the receiver\nfunc (o * const Object) ImmutableMethod() const * const Object {\n    o.MutatingMethod()                      // Compile-time error\n    o.mutableField = \u0026Object{}              // Compile-time error\n    o.mutableField.mutableField = \u0026Object{} // Compile-time error\n    return o.mutableField\n}\n\nfunc main() {\n    obj := * const Object (\u0026Object{})\n    obj.ImmutableMethod()\n    obj.MutatingMethod() // Compile-time error\n}\n```\n**Expected compilation errors:**\n```\n.example.go:15:7 cannot call mutating method (Object.MutatingMethod) on immutable o (type * const Object)\n.example.go:16:21 cannot assign to contextually immutable field (Object.mutableField) of o (type * const Object)\n.example.go:17:34 cannot assign to contextually immutable field (Object.mutableField) of o (type * const Object)\n.example.go:24:9 cannot call mutating method (Object.MutatingMethod) on immutable obj (type * const Object)\n```\n\n----\n\n### 2.3. Immutable Arguments\nImmutable types can be used to guarantee the immutability of function\narguments.\n\n```go\ntype Object struct {\n\tMutableField *Object // Mutable\n}\n\n// MutatingMethod is a non-const method\nfunc (o *Object) MutatingMethod() {}\n\n// ImmutableMethod is a const method\nfunc (o * const Object) ImmutableMethod() {}\n\n// MutateObject mutates the given object\nfunc MutateObject(obj *Object) {\n\tobj.MutableField = \u0026Object{}\n}\n\n// ReadObj is guaranteed to only read the object passed by the argument\n// without mutating it in any way\nfunc ReadObj(\n\tobj * const Object // Mutable reference to immutable object\n) {\n\tobj = nil // fine, because the pointer is mutable\n\n\tMutateObject(obj)            // Compile-time error\n\tobj.MutatingMethod()         // Compile-time error\n\tobj.MutableField = \u0026Object{} // Compile-time error\n}\n```\n**Expected compilation errors:**\n```\n.example.go:23:19 cannot use obj (type * const Object) as type *Object in argument to MutateObject\n.example.go:24:9 cannot call mutating method (Object.MutatingMethod) on immutable obj (type * const Object)\n.example.go:25:23 cannot assign to contextually immutable field (Object.MutableField) of obj (type * const Object)\n```\n\n----\n\n### 2.4. Immutable Return Values\nImmutable types can be used to guarantee the immutability of a\nfunction's returned values.\n\n```go\ntype Object struct {\n\tMutableField *Object // Mutable\n}\n\n// MutatingMethod is a non-const method\nfunc (p *Object) MutatingMethod() {\n\tp.MutableField = \u0026Object{}\n}\n\n// ReturnImmutable returns an immutable value\nfunc ReturnImmutable() const * const Object {\n\treturn \u0026Object{\n\t\tMutableField: \u0026Object{\n\t\t\tMutableField: \u0026Object{},\n\t\t},\n\t}\n}\n\nfunc main() {\n\timmutableVariable := ReturnImmutable()\n\t\n\timmutableVariable.MutableField = \u0026Object{} // Compile-time error\n\timmutableVariable.MutatingMethod()         // Compile-time error\n}\n```\n**Expected compilation errors:**\n```\n.example.go:22:37 cannot assign to contextually immutable field (Object.MutableField) of immutable immutableVariable (type const * const Object)\n.example.go:23:23 cannot call mutating method (Object.MutatingMethod) on immutable immutableVariable (type const * const Object)\n```\n\n----\n\n### 2.5. Immutable Variables\nImmutable types can be used to declare immutable variables.\n\n```go\ntype Object struct {\n\tMutableField *Object // Mutable\n}\n\n// MutatingMethod is a non-const method\nfunc (o *Object) MutatingMethod() {}\n\n// ImmutableMethod is a const method\nfunc (o * const Object) ImmutableMethod() {}\n\n// NewObject creates and returns a new mutable object instance\nfunc NewObject() *Object {\n\treturn \u0026Object{}\n}\n\n// MutateObject mutates the given object\nfunc MutateObject(obj *Object) {\n\tobj.MutableField = \u0026Object{}\n}\n\n// ConstRef helps shortening declaration statements\ntype ConstRef const * const Object\n\nfunc main() {\n\t// The definition version:\n\t// The cast is necessary because NewObject returns a mutable value\n\t// while we want an immutable variable\n\tobj := const * const Object (NewObject())\n\n\t// The var declaration version:\n\t// (this statement could be shortened using a type alias)\n\tvar obj_var_long const * const Object = NewObject()\n\tvar obj_var ConstRef = NewObject()\n\n\tobj.MutableField = \u0026Object{} // Compile-time error\n\tobj.MutatingMethod()         // Compile-time error\n\tMutateObject(obj)            // Compile-time error\n}\n```\n**Expected compilation errors:**\n```\n.example.go:23:23 cannot assign to contextually immutable field (Object.MutableField) of obj (type const * const Object)\n.example.go:24:9 cannot call mutating method (Object.MutatingMethod) on immutable obj (type const * const Object)\n.example.go:25:19 cannot use obj (type const * const Object) as type *Object in argument to MutateObject\n```\n\n### 2.6. Immutable Interface Methods\nInterfaces can be obliged to require receiver type immutability using the\n`const` qualifier in the method declaration to prevent the implementing function\nfrom mutating the object referenced by the interface.\n```go\n// Interface represents a strict interface with an immutable method\ntype Interface interface {\n\t// Read must not mutate the underlying implementation\n\tconst Read() string\n\n\t// Write can potentially mutate the underlying implementation\n\tWrite(string)\n}\n\n// ValidImplementation represents a correct implementation of Interface\ntype ValidImplementation struct {\n\t/*...*/\n}\n\n// Read correctly implements Interface.Read, it has an immutable receiver\nfunc (r * const ValidImplementation) Read() string {\n\t/*...*/\n}\n\n// Write correctly implements Interface.Write,\n// even though the receiver is immutable\nfunc (r * const ValidImplementation) Write(s string) {\n\t/*...*/\n}\n\n// InvalidImplementation represents an incorrect implementation of Interface\ntype InvalidImplementation struct {\n\t/*...*/\n}\n\n// Read incorrectly implements the immutable Interface.Read,\n// the receiver must be of type: * const InvalidImplementation\nfunc (r * InvalidImplementation) Read() string {\n\t/*...*/\n}\n\n// Write correctly implements Interface.Write\nfunc (r * InvalidImplementation) Write(s string) {\n\t/*...*/\n}\n\nfunc main() {\n\tvar iface Interface = \u0026InvalidImplementation{} // Compile-time error\n\tiface.Write(0, \"example\")\n\n\tvar readOnlyIface const Interface = \u0026ValidImplementation{}\n\treadOnlyIface.Read()\n\treadOnlyIface.Write() // Compile-time error\n}\n```\n```\n.example.go:43:26: cannot use InvalidImplementation literal (type *InvalidImplementation) as type Interface in assignment:\n\t*InvalidImplementation does not implement Interface (Read method has mutable pointer receiver, expected an immutable receiver type)\n.example.go:48:19: cannot call mutating method (Interface.Write) on immutable readOnlyIface (type const Interface)\n```\n\n----\n\n### 2.7. Slice Aliasing\nImmutability of slices is always inherited from their parent slice. Sub-slicing\nimmutable slices results in new immutable slices:\n```go\nfunc ReturnConstSlice() const []int {\n\treturn []int {1, 2, 3}\n}\n\nfunc main() {\n\toriginalSlice := const([]int{1, 2, 3})\n\tsubSlice := originalSlice[:1]\n\n\toriginalSlice[0] = 4 // Compile-time error\n\tsubSlice[0] = 4      // Compile-time error\n\n\tanotherSubSlice := ReturnConstSlice()[:1]\n\tanotherSubSlice[0] = 4 // Compile-time error\n}\n```\n```\n.example.go:9:23 cannot assign to immutable originalSlice (type const []int)\n.example.go:10:18 cannot assign to immutable subSlice (type const []int)\n.example.go:13:25 cannot assign to immutable anotherSubSlice (type const []int)\n```\n\n### 2.8. Address Operators\n\n#### 2.8.1. Taking the address of a variable\nTaking the address of an *immutable* variable results in a *mutable* pointer to\nan *immutable* object:\n\n```go\nvar t const T = T{}\nt_pointer := \u0026t // * const T\n```\n\nTo take an *immutable* pointer from an *immutable* variable explicit casting is\nnecessary:\n```go\nvar t const T = T{}\nt_pointer1 := const(\u0026t) // const * const T\n\n// Or like this:\nt_pointer2 := const * const T(\u0026t) // const * const T\n```\n\n#### 2.8.2. Dereferencing a pointer\nDereferencing an *immutable* pointer to an *immutable* object:\n```go\nt := const * const T (\u0026T{})\n*t // const T\n```\n\nDereferencing a mutable pointer to an *immutable* object:\n```go\nt := * const T (\u0026T{})\n*t // const T\n```\n\nDereferencing an *immutable* pointer to a *mutable* object:\n```go\nt := const * T (\u0026T{})\n*t // T\n```\n\n### 2.9. Immutable Reference Types\nReference types such as [slices](https://golang.org/ref/spec#Slice_types),\n[maps](https://golang.org/ref/spec#Map_types),\n[channels](https://golang.org/ref/spec#Channel_types) and\n[pointers](https://golang.org/ref/spec#Pointer_types) can also be declared\nimmutable using the `const` qualifier just like any other type. But the objects\n/ items referenced by immutable reference types **don't inherit their\nimmutability!** Reference types can point to both mutable and immutable types,\nthis makes the type system very versatile and flexible.\n\nThe examples below demonstrate a few possible combinations:\n\n#### 2.9.1. Pointer Examples\n\n##### 2.9.1.1. Immutable pointer to a mutable object\n\n```go\nvar immut2mut const *Object = \u0026Object{}\n\nimmut2mut = \u0026Object{} // Compile-time error\nimmut2mut.Field = 42  // fine\nimmut2mut.Mutation()  // fine\n```\n\n##### 2.9.1.2. Mutable pointer to an immutable object\n\n```go\nvar mut2immut * const Object = \u0026Object{}\n\nmut2immut = \u0026Object{} // fine\nmut2immut.Field = 42  // Compile-time error\nmut2immut.Mutation()  // Compile-time error\n```\n\n##### 2.9.1.3. Immutable pointer to an immutable object\n```go\nvar immut2immut const * const Object = \u0026Object{}\n\nimmut2immut = \u0026Object{} // Compile-time error\nimmut2immut.Field = 42  // Compile-time error\nimmut2immut.Mutation()  // Compile-time error\n```\n\n#### 2.9.2. Slice Examples\n\n##### 2.9.2.1. Immutable slice of immutable objects\n\n```go\nvar immut2immut const [] const Object\nimmut2immut = append(immut2immut, Object{}) // Compile-time error\nimmut2immut[0] = Object{}                   // Compile-time error\n\nobj := immut2immut[0]\nobj.Mutation() // Compile-time error\n```\n\n##### 2.9.2.2. Mutable slice of immutable objects\n\n```go\nvar mut2immut [] const Object\nmut2immut = append(mut2immut, Object{}) // fine\nmut2immut[0] = Object{}                 // fine\n\nobj := mut2immut[0]\nobj.Mutation() // Compile-time error\n```\n\n##### 2.9.2.3. Immutable slice of mutable objects\n\n```go\nvar immut2mut const [] Object\nimmut2mut = append(immut2mut, Object{}) // Compile-time error\nimmut2mut[0] = Object{}                 // Compile-time error\n\nobj := immut2mut[0]\nobj.Mutation() // fine\n```\n\n##### 2.9.2.4. Mutable slice of mutable objects\n\n```go\nvar mut2mut [] Object\nmut2mut = append(mut2mut, Object{}) // fine\nmut2mut[0] = Object{}               // fine\n\nobj := mut2mut[0]\nobj.Mutation() // fine\n```\n\n#### 2.9.3. Map Examples\n\n##### 2.9.3.1. Mutable map of immutable keys to mutable objects\n\n```go\nvar mut_immut2mut map[const Object] Object\n\nnewKey := Object{}\nmut_immut2mut[newKey] = Object{} // fine\ndelete(mut_immut2mut, newKey)    // fine\n\nfor key, value := range mut_immut2mut {\n\tkey.Mutation()   // Compile-time error\n\tvalue.Mutation() // fine\n}\n```\n\n##### 2.9.3.2. Mutable map of mutable keys to immutable objects\n\n```go\nvar mut_mut2immut map[Object] const Object\n\nnewKey := Object{}\nmut_mut2immut[newKey] = Object{} // fine\ndelete(mut_mut2immut, newKey)    // fine\n\nfor key, value := range mut_mut2immut {\n\tkey.Mutation()   // fine\n\tvalue.Mutation() // Compile-time error\n}\n```\n\n##### 2.9.3.3. Mutable map of immutable keys to immutable objects\n\n```go\nvar immut_immut2immut map[const Object] const Object\n\nnewKey := Object{}\nimmut_immut2immut[newKey] = Object{} // fine\ndelete(immut_immut2immut, newKey)    // fine\n\nfor key, value := range immut_immut2immut {\n\tkey.Mutation()   // Compile-time error\n\tvalue.Mutation() // Compile-time error\n}\n```\n\n##### 2.9.3.4. Immutable map of immutable keys to immutable objects\n\n```go\nvar m const map[const Object] const Object\n\nnewKey := Object{}\nm[newKey] = Object{} // Compile-time error\ndelete(m, newKey)    // Compile-time error\n\nfor key, value := range m {\n\tkey.Mutation()   // Compile-time error\n\tvalue.Mutation() // Compile-time error\n}\n```\n\n#### 2.9.4. Channel Examples\n\n##### 2.9.4.1. Immutable channels of immutable objects\n\n```go\nfunc main() {\n\tch := ConstReadOnlyChannel()\n\tch = AnotherChannelOfSameType() // Compile-time error\n\n\timmutObj := \u003c-ch\n\timmutObj.Field = 42  // Compile-time error\n\timmutObj.Mutation()  // Compile-time error\n}\n\n// ConstReadOnlyChannel returns an immutable read only channel\n// of immutable objects\nfunc ConstReadOnlyChannel() const \u003c-chan const Object {\n\tch := make(chan Object)\n\tgo func() {\n\t\tch \u003c- Object{}\n\t}()\n\treturn ch\n}\n```\n\n##### 2.9.4.2. Immutable channels of mutable objects\n\n```go\nfunc main() {\n\tch := ConstReadOnlyChannel()\n\tch = AnotherChannelOfSameType() // Compile-time error\n\n\tmutObj := \u003c-ch\n\tmutObj.Field = 42  // fine\n\tmutObj.Mutation()  // fine\n}\n\n// ConstReadOnlyChannel returns an immutable read only channel\n// of mutable objects\nfunc ConstReadOnlyChannel() const \u003c-chan Object {\n\tch := make(chan Object)\n\tgo func() {\n\t\tch \u003c- Object{}\n\t}()\n\treturn ch\n}\n```\n\n##### 2.9.4.3. Mutable channels of immutable objects\n\n```go\nfunc main() {\n\tch := MutReadOnlyChannel()\n\tch = MutReadOnlyChannel() // fine\n\n\timmutObj := \u003c-ch\n\timmutObj.Field = 42  // Compile-time error\n\timmutObj.Mutation()  // Compile-time error\n}\n\n// MutReadOnlyChannel returns a mutable read only channel of immutable objects\nfunc MutReadOnlyChannel() \u003c-chan const Object {\n\tch := make(chan Object)\n\tgo func() {\n\t\tch \u003c- Object{}\n\t}()\n\treturn ch\n}\n```\n\n### 2.10. Immutable Package-Scope Variables\nPackage-scope variables of an immutable type can be used similarly to\npackage-scope constants. They compensate for the [lack of non-scalar\nconstants](https://github.com/romshark/Go-1-2-Proposal---Immutability#114-inconsistent-concept-of-constants).\n\n```go\npackage library\n\ntype T struct {}\n\nfunc (t * const T) MutatingMethod() {\n\t/*...*/\n}\n\n// ConstantNames represents a package-scope immutable slice of strings\nvar ConstantNames const []string = []string{\"Anna\", \"Mike\", \"Ashley\"}\n\n// privateImmutTInstances represents a package-scope immutable slice of pointers\n// to immutable instances of T\nvar privateImmutTInstances const [] * const T = []*T{\n\t\u0026T{},\n\t\u0026T{},\n\t\u0026T{},\n}\n\n// Function represents an exported function that tries to mutate immutable\n// package-scope variables\nfunc Function() {\n\tConstantNames[0] = \"Hannah\"      // Compile-time error\n\tprivateImmutTInstances[0] = \u0026T{} // Compile-time error\n\tconstT := privateImmutTInstances[0]\n\tconstT.MutatingMethod() // Compile-time error\n}\n```\n```\n.library.go:23:23: cannot assign to immutable ConstantNames (type const []string)\n.library.go:24:32: cannot assign to immutable privateImmutTInstances (type const [] * const T)\n.library.go:26:12: cannot call mutating method (T.MutatingMethod) on immutable constT (type * const T)\n```\n\nImported:\n```go\npackage main\n\nimport \"github.com/x/library\"\n\nfunc main() {\n\tlibrary.ConstantNames[0] = \"Hannah\"      // Compile-time error\n}\n```\n```\n.library.go:6:31: cannot assign to immutable library.ConstantNames (type const []string)\n```\n\n### 2.11. Explicit Type Casting\nMutable types are always implicitly cast to their immutable counterparts but in\nsome situations explicit casting may also be useful.\n\n#### 2.11.1. Simple Cast\nSimple typecasting `const(mt)` converts a mutable type into its immutable\ncounterpart:\n\n```go\n// Simple non-const to const casting\nconst_string := const(\"test\") // const string\n\nconst_pointer := const(\u0026T{}) // const * T\n\nconst_slice := const([]int{1, 2, 3}) // const []int\n}\n```\n\nApplying simple typecasting to already immutable types has no effect.\n\n#### 2.11.2. Literal Type Casting\nFor more complex types simple `const` casting is insufficient, thus a literal\ntype cast `immutable type (symbol)` to an immutable type is required.\n\n```go\nvar original_slice [] [] *T\n\n// Mutable slice of immutable slices of pointers to a mutable instance of T\ns1 := [] const [] * T (original_slice)\n\n// Immutable slice of mutable slices of pointers to an immutable instance of T\ns2 := const [] [] * const T (original_slice)\n\n// Mutable slices of mutable slices of pointers to an immutable instance of T\ns3 := [] [] * const T (original_slice)\n\n// Immutable slice of immutable slices of pointers to an immutable instance of T\ns4 := const [] const [] * const T (original_slice)\n\nvar original_map map[*T]*T\n\n// Immutable map of:\n// pointers to an immutable instance of T (key) to:\n// pointers to a mutable instance of T (value)\nm1 := const map [* const T] * T (original_map)\n\n// Immutable map of:\n// pointers to a mutable instance of T (key) to:\n// pointers to an immutable instance of T (value)\nm2 := const map [* T] * const T (original_map)\n\n// Mutable map of:\n// pointers to an immutable instance of T (key) to:\n// pointers to an immutable instance of T (value)\nm3 := map [* const T] * const T (original_map)\n```\n\n#### 2.11.3. Prohibition of Casting Immutable- to Mutable Types\nCasting immutable types to mutable types is forbidden because it would make it\npossible to silently void the immutability guarantee breaking the entire concept\nof immutability.\n\n### 2.12. Implicit Casting\nMutable types are implicitly cast to their immutable counterparts. This rule is\napplied to any type in a type-chain. If we consider the definition of a type as\na binary sequence where mutable types are represented by `0` and immutable types\nby `1`, then any conversions of `1` to `0` should cause a compile-time error.\n\n```go\n// tip: use an 80-column wide view to make sense of the markers\n\n//         0_ 0_ 0_ 0 1______\nvar origin [] [] [] * const T\n\n/* LEGAL CONVERSIONS */\n\n//       1_______ 0_ 0_ 0 1______\nvar var1 const [] [] [] * const T = origin // 00001 -\u003e 10001\n\n//       0_ 0_ 1_______ 0 0\nvar var2 [] [] const [] * T = origin // 00001 -\u003e 00100\n\n//       0_ 1_______ 1_______ 0 0\nvar var3 [] const [] const [] * T = origin // 00001 -\u003e 01100\n\n/* ILLEGAL CONVERSIONS */\n\n//       0_ 1_______ 0_ 0 0                 F        F\nvar inv1 [] const [] [] * T = origin // 00001 -\u003e 01000\n\n//       1_______ 1_______ 1_______ 1______ 0                 F        F\nvar inv2 const [] const [] const [] const * T = origin // 00001 -\u003e 11110\n```\n\n#### 2.12.1. Implicit Casting of Pointer Receivers\nPointer receivers are implicitly cast in both directions (mutable to immutable\nand vice-versa) when the types they're pointing to are equal.\n\nMethods:\n```go\ntype T struct {/*...*/}\nfunc (r1 *T) M1() {/*...*/}\nfunc (r2 const * T) M2() {/*...*/}\nfunc (r3 * const T) M3() {/*...*/}\nfunc (r4 const * const T) M4() {/*...*/}\n```\n\nVariables:\n```go\n// mutable pointer to mutable T\nt1 := \u0026T{}\n\n// immutable pointer to mutable T\nvar t2 const * T = \u0026T{}\n\n// mutable pointer to immutable T\nvar t3 * const T = \u0026T{}\n\n// immutable pointer to immutable T\nvar t4 const * const T = \u0026T{}\n```\n\n| Combination | Compile-time Result | Reason |\n|-|-|-|\n| `t1.M1()` | legal | types match. |\n| `t2.M1()` | **implicit cast** |  `const * T` (`t2`) is implicitly cast to `* T` (`r1`) because in both cases `T` is mutable. |\n| `t3.M1()` | illegal | `T` referenced by `t3` is immutable, but `M1` is a mutating method. |\n| `t4.M1()` | illegal | `T` referenced by `t4` is immutable, but `M1` is a mutating method. |\n| `t1.M2()` | **implicit cast** | `* T` (`t1`) is implicitly cast to `const * T` (`r2`) because in both cases `T` is mutable. |\n| `t2.M2()` | legal | types match. |\n| `t3.M2()` | illegal | `T` referenced by `t3` is immutable, but `M2` is a mutating method. |\n| `t4.M2()` | illegal | `T` referenced by `t4` is immutable, but `M2` is a mutating method. |\n| `t1.M3()` | **implicit cast**  | `* T` (`t1`) is implicitly cast to `* const T` (`r3`) because `T` is mutable and `M3` is a non-mutating method. |\n| `t2.M3()` | illegal | `T` referenced by `t2` is mutable, but `M3` is a mutating method. |\n| `t3.M3()` | legal | types match. |\n| `t4.M3()` | **implicit cast** | `const * const T` (`t4`) is implicitly cast to `* const T` (`r3`) because in both cases `T` is immutable. |\n| `t1.M4()` | **implicit cast** | `* T` (`t1`) is implicitly cast to `const * const T` (`r4`) because `T` is mutable and `M3` is a non-mutating method. |\n| `t2.M4()` | **implicit cast** | `const * T` (`t2`) is implicitly cast to `const * const T` (`r4`) because `T` is mutable and `M4` is a non-mutating method. |\n| `t3.M4()` | **implicit cast** | `* const T` (`t3`) is implicitly cast to `const * const T` (`r4`) because in both cases `T` is immutable. |\n| `t4.M4()` | legal | types match. |\n\n### 2.12. Standard Library\nMinimal backward-compatible changes to the standard library need to be made to\nmake user-written code that takes advantage of immutable types interoperable\nwith the standard library.\n\n[strings.Join](https://golang.org/pkg/strings/#Join) is a typical example of a\nstandard library function that needs to be updated to take advantage of\nimmutable types. Its updated version would have to guarantee the immutability of\n`a`:\n\n```go\nfunc Join(a const []string, sep string) string\n```\n\nOptionally, `sep` could be declared immutable as well (`const string`)\nprotecting it from accidental overwrites in the function scope. Making `sep`\nimmutable simply guides the function's implementor by telling him/her that `sep`\nis not meant to be overwritten in the function's scope, it has no meaning for\nthe function's caller though.\n\nThis change is necessary because otherwise, the following code that takes\nadvantage of immutable types would not compile:\n\n```go\n// Example won't compile because the old strings.Join takes a mutable slice,\n// but casting the immutable \"a\" to a mutable slice of strings is illegal!\nfunc Example(a const []string) {\n\tconcat := strings.Join(a, \",\") // Compile-time error\n}\n```\n\nIf [strings.Join](https://golang.org/pkg/strings/#Join) won't support immutable\ntypes, then its users will be **forced** to fall back to a mutable slice\nargument, which makes the immutability concept useless for their specific case.\n\n## 3. Immutability by Default (Go \u003e= 2.x)\nIf we were to think of an immutability proposal for the backward-incompatible Go\n2 language specification, then making all types immutable by default and\nintroducing a special keyword `mut` for mutability qualification would be a\nbetter option.\n\n```go\n// Object implements the ObjectInterface interface\ntype Object struct {\n\tImmutable_str string\n\tMutable_str   mut string\n\n\tImmutable_immutRef_to_immutObj * Object\n\tMutable_mutRef_to_immutObj     mut * Object\n\tMutable_mutRef_to_mutObj       mut * mut Object\n\n\tImmutable_immutSlice_of_immutObj [] Object\n\tMutable_mutSlice_of_immutObj     mut [] Object\n\tMutable_mutSlice_of_mutObj       mut [] mut Object\n\n\tImmutable_immutMap_of_immutObj map[Object] Object          // immutable key\n\tMutable_mutMap_of_immutObj     mut [Object] Object         // immutable key\n\tMutable_mutMap_of_mutObj       mut [mut Object] mut Object // mutable key\n}\n\n// MutableMethod implements ObjectInterface.MutableMethod\nfunc (mutableReceiver * mut Object) MutableMethod(\n\tmutableArgument mut * Object, // mutable reference to immutable object\n) (\n\tmutableReturnValue mut * mut Object, // mutable reference to mutable object\n) {\n\tvar mutRef_to_mutObj mut * mut Object\n\tvar mutRef_to_immutObj mut * Object\n\tvar immutRef_to_immutObj * Object\n\n\treturn nil\n}\n\n// ImmutableMethod implements ObjectInterface.ImmutableMethod\nfunc (immutableReceiver *Object) ImmutableMethod(\n\timmutableArgument * Object, // immutable reference to immutable object\n) (\n\timmutableReturnValue * Object // immutable reference to immutable object\n) {\n\tvar mutRef_to_mutObj mut * mut Object\n\tvar mutRef_to_immutObj mut * Object\n\tvar immutRef_to_immutObj * Object\n\n\treturn nil\n}\n\ntype ObjectInterface interface {\n\tmut MutableMethod(arg mut * Object) (returnValue mut * mut Object)\n\tImmutableMethod(arg *Object) (returnValue *Object)\n}\n```\n\n### 3.1. Benefits\n\n#### 3.1.1. Safety by Default\nIt's easy to forget to add the `const` qualifier and accidentally make something\nmutable. But when mutable types need to be explicitly declared mutable using the\n`mut` qualifier writing code becomes even safer.\n\n#### 3.1.2. Less Code\nStatistically, Most of the variables, arguments, fields, return values and\nmethods are immutable, thus the frequent `const` qualifiers can be replaced by\nfewer `mut` qualifiers, which improves both readability and coding speed. The\n`mut` keyword is also shorter than `const`.\n\n#### 3.1.3. No `const` Keyword Overloading\nThe need for overloading of the `const` keyword would vanish, which would\nimprove semantic language consistency.\n\n## 4. FAQ\n\n### 4.1. Are the items within immutable slices/maps also immutable?\n**No**, they're not! As stated in [Section 2.9.](#29-immutable-reference-types),\nan immutable slice/map of mutable objects is declared this way:\n```go\ntype ImmutableSlice const []*Object\ntype ImmutableMap   const map[*Object]*Object\n```\nIf you want the items of an immutable slice/map to be immutable as well, you'll\nneed to declare them using the `const` qualifier:\n```go\ntype ImmutableSlice     const [] const * const Object\ntype ImmutableMap       const map[*Object] const * const Object\ntype ImmutableMapAndKey const map[const * const Object] const * const Object\n```\nA deeply-immutable matrix could, therefore, be declared the following way:\n```go\ntype ImmutableMatrix const [] const [] int\n```\n\n---\n\n### 4.2. Go is all about simplicity, so why make the language more complicated?\nThe `const` qualifier adds only a little cognitive overhead:\n- When declaring a **function argument** we have to know whether we want to be\n  able to change its state and make it immutable if we don't.\n- When declaring a **struct field** we have to know, whether we want the state\n  of this field to remain unchangeable, during the lifetime of an object\n  instantiated from this struct, as soon as it's initialized.\n- When declaring a **return value** we have to know whether we want to give the\n  caller the permission to modify the object we returned.\n- When declaring a **variable** we have to know, whether we want to change it\n  in this context.\n- When declaring a **function-receiver** we have to know, whether this function\n  will change anything inside the receiver.\n- When declaring an **interface method** we have to know, whether this method\n  should not change the state of the object implementing this interface.\n- When declaring a **reference type** such as a pointer, a slice a map or a\n  channel we have to know whether we want to:\n\t- make the object changeable, but not its reference\n\t- make the actual reference changeable, but not the object it references\n\t- make both the reference and the object changeable\n\nThis additional cognitive overhead prevents us from introducing the complexity\ncreated by mutable shared state. Bugs introduced through mutable shared state\nare very dangerous, hard to notice, hard to identify and pretty hard to fix.\nJustifying the simplicity of a language which can lead to very complex bugs is\nrather incorrect when considering the insignificant overhead of the `const`\nqualifier. Thus, immutability is a feature, the overhead of which outweighs the\ndisadvantages of not having it.\n\n**Example:** You always have to remember to copy stuff that you don't want\nothers to be able to mutate, or at least explicitly advise to \"not mutate\ncertain stuff\" in the documentation running the risk of breaking your\ninattentive colleague's code:\n\n```go\n// ConnectedClients returns the list of all currently connected clients.\n// DO NOT mutate the returned slice, this could break the server!\nfunc (s *Server) ConnectedClients() []Client {\n\treturn s.clients\n}\n```\nBut even if the people working with your code follow your advices, they could\nstill mess it up:\n```go\n// ThisWontMutateIt verbally promises to\n// not mutate the given slice of clients\nfunc ThisWontMutateIt(clients []Client) {\n\t// You know what? to hell with the promise!\n\t// I don't know where this slice originated from, so why care?\n\tclients[len(clients) - 1] = nil\n}\n\nclients = server.ConnectedClients()\n// It promised not to mutate it, so it's safe, right? right!?\nThisWontMutateIt(clients)\n```\n\nTo improve the safety of the code above, we'd usually return a copy instead:\n```go\nfunc (s *Server) ConnectedClients() []Client {\n\t// Copy the client list to avoid returning an unsafe mutable reference\n\tclients := make([]Client, len(s.clients))\n\tfor i, clt := range s.clients {\n\t\tclients[i] = clt\n\t}\n\treturn clients\n}\n```\nThis certainly makes both code and documentation more complicated and error\nprone (and slower) than it could be with immutability:\n```go\n// ConnectedClients returns the list of all currently connected clients.\nfunc (s * const Server) ConnectedClients() const []Client {\n\treturn s.clients\n}\n```\n\n---\n\n### 4.3. Aren't other features such as generics and better error handling not more important right now?\nUnlike other language specification issues such as *\"generics\"* and *\"how to\nhandle errors more elegantly\"* there's really not much to argue about in case of\nimmutability. It should be clear that:\n- it makes code both safer and easier to make sense of,\n- it doesn't require any breaking changes,\n- it doesn't even require a single new language keyword.\n\nTherefore immutability should be considered of higher priority compared to other\nlanguage design proposals.\n\n----\n\n### 4.4. Why overload the `const` keyword instead of introducing a new keyword like `immutable` etc.?\n**Backwards-compatibility**. Using the const keyword would allow us to introduce\nimmutability to Go 1.x without having to make breaking changes to the language.\nThe introduction of a new keyword could potentially break existing Go 1.x code,\nwhere the new keyword might be used for naming symbols causing build conflicts.\n`const` on the other hand is already a [reserved language\nkeyword](https://golang.org/ref/spec#Keywords) which doesn't interfere with the\nproposed language changes and verbally comes close to the desired meaning (for\nexample, C++ uses the `const` keyword to do just that).\n\n----\n\n### 4.5. How are constants different from immutable types?\n**Short:** Constants are static in memory, while immutable types are just\nwrite-protected references to mutable memory.\n\n**Long:** The value of a constant is defined during the compilation and remains\na static piece of memory for the entire lifetime of your program. An immutable\nfield, argument, return value, receiver or variable, on the other hand, is\n**not** static in memory, because it can still be mutated through mutable\nreferences:\n\n```go\n// CreateList creates a new slice and returns both, a mutable and an immutable\n// reference to it (which is bad! don't ever do that!\n// unless you know what you're after!)\nfunc CreateList(size int) (mutable []string, immutable const []string) {\n\tnewSlice := make([]string, size)\n\tfor i := 0; i \u003c size; i++ {\n\t\tnewSlice[i] = \"sample text\"\n\t}\n\treturn newSlice, newSlice\n}\n\nfunc main() {\n\tmutableReference, immutableReference := CreateList(10)\n\n\t// Mutating an immutable return value is illegal\n\timmutableReference[5] = \"mutated\" // Compile-time error\n\n\t// Mutating the underlying array through a mutable reference is just fine!\n\tmutableReference[5] = \"mutated\"\n\n\t// You can now observe the mutation from the read-only immutable reference\n\timmutableReference[5] // \"mutated\"\n}\n```\n\n**NOTICE:** the above code is **bad code**! Its purpose was to demonstrate that\nimmutable types are not constants. If you want to prevent immutable objects from\nbeing mutated for sure - drop all mutable references to it as soon as it's\ncreated!\n\n----\n\n### 4.6. Why do we need immutable receivers if we already have copy-receivers?\nThere are two reasons: safety and performance.\n\n**Copy-receivers don't prevent mutations!** They simply can't because of\n[pointer aliasing](https://en.wikipedia.org/wiki/Pointer_aliasing):\n\n```go\ntype Object struct {\n\tname     string\n\tparent   *Object\n\tchildren []*Object\n}\n\n// SetName is a non-const mutating method\nfunc (o *Object) SetName(newName string) {\n\to.name = newName\n}\n\n// ReadOnlyMethod is insidious because it verbally \"promises\"\n// to not touch the object while it still can do!\n// Even though it has a non-pointer receiver, the copy\n// can't get rid of aliasing and can thus mutate internals!\nfunc (o Object) ReadOnlyMethod() {\n\tif len(o.children) \u003e 0 {\n\t\t// Mutating contextually immutable aliases is legal, VERY BAD!\n\t\to.children[0] = nil\n\t}\n\tif o.parent != nil {\n\t\t// Call non-const method in immutable context is legal, VERY BAD!\n\t\to.parent.SetName(\"GOTCHA!\")\n\t}\n}\n\nfunc main() {\n\tobj := \u0026Object{\n\t\tname:   \"root\",\n\t\tparent: nil,\n\t}\n\tobj.children = []*Object{\n\t\t\u0026Object{\n\t\t\tname:   \"child_1\",\n\t\t\tparent: obj,\n\t\t},\n\t\t\u0026Object{\n\t\t\tname:   \"child_2\",\n\t\t\tparent: obj,\n\t\t},\n\t}\n\n\tfmt.Println(\"Root before:  \", obj)\n\tfmt.Println(\"Child before: \", obj.children[0])\n\n\tfirstChild := obj.children[0]\n\tfirstChild.ReadOnlyMethod() // Will mutate parent's name\n\n\tobj.ReadOnlyMethod() // Will mutate children list\n\n\tfmt.Println(\"Root after:   \", obj)        // Children list mutated\n\tfmt.Println(\"Child after:  \", firstChild) // Root name mutated\n}\n```\nhttps://play.golang.org/p/0kRSuVFkSMN\n\n**Copy-receivers are slow(er)**. Consider the following benchmark:\n```go\ntype Object struct {\n\tname    string\n\ttext    []rune\n\tdouble  float64\n\tinteger int64\n\tbytes   []byte\n}\n\n// PointerReceiver has a pointer receiver\nfunc (o *Object) PointerReceiver() (string, []rune, float64) {\n\treturn o.name, o.text, o.double\n}\n\n// CopyReceiver has a copy receiver\nfunc (o Object) CopyReceiver() (string, []rune, float64) {\n\treturn o.name, o.text, o.double\n}\n\nvar name string\nvar text []rune\nvar double float64\n\n// BenchmarkPointerReceiver benchmarks the pointer-receiver method\nfunc BenchmarkPointerReceiver(b *testing.B) {\n\tobj := \u0026Object{}\n\tb.ResetTimer()\n\tfor n := 0; n \u003c b.N; n++ {\n\t\tname, text, double = obj.PointerReceiver()\n\t}\n}\n\n// BenchmarkCopyReceiver benchmarks the copy-receiver method\nfunc BenchmarkCopyReceiver(b *testing.B) {\n\tobj := \u0026Object{}\n\tb.ResetTimer()\n\tfor n := 0; n \u003c b.N; n++ {\n\t\tname, text, double = obj.CopyReceiver()\n\t}\n}\n```\nhttps://play.golang.org/p/2xgn7YMosXO\n\nThe results should be similar to:\n```\ngoos: windows\ngoarch: amd64\npkg: benchreceiver\nBenchmarkPointerReceiver-12     1000000000               2.12 ns/op\nBenchmarkCopyReceiver-12        300000000                4.23 ns/op\nPASS\nok      benchreceiver   4.110s\n```\n_Windows 10; i7 3930K @ 3.80 Ghz_\n```\ngoos: darwin\ngoarch: amd64\npkg: github.com/romshark/benchreceiver\nBenchmarkPointerReceiver-8    1000000000          2.05 ns/op\nBenchmarkCopyReceiver-8       300000000           4.62 ns/op\nPASS\nok      benchreceiver   4.129s\n```\n_MacOS High Sierra (10.13); i7-4850HQ @ 2.30 GHz_\n\nEven though ~2 nanoseconds doesn't sound like much it's still twice as\nexpensive to call.\n\n**Conclusion:** copy-receivers are not a solution, they make your code slower\n**without** providing any protection from [pointer\naliasing](https://en.wikipedia.org/wiki/Pointer_aliasing), thus immutable\nreceivers (be it an immutable copy or an immutable pointer receiver) are\nnecessary to ensure compiler-enforced safety.\n\n### 4.7. Why do we need immutable interface types?\nImmutable interface types allow us to **reuse** interface types disabling their\nmutating ability in certain scopes without having to define two separate\ninterface types (one interface type with read methods only and another one with\nboth mutating and non-mutating methods)\n\nPassing an immutable interface to a function as an argument while trying to call\na mutating method on it, for example, would generate a compile-time error:\n```go\ntype Interface {\n\tconst ReadOnly()\n\tWrite()\n}\n\ntype Implementation struct {}\nfunc (i * const Implementation) ReadOnly() {}\nfunc (i * Implementation) Write() {}\n\n// TakeReadInterface will not be able to execute non-const methods of the interface\nfunc TakeReadInterface(iface const Interface) {\n\tiface.ReadOnly() // fine\n\tiface.Write()    // Compile-time error\n}\n\nfunc main() {\n\tiface := \u0026Implementation{}\n\tTakeReadInterface(iface)\n}\n```\n```\n.example:13:11: cannot call mutating method (Interface.Write) on immutable iface (type const Interface)\n```\n\n### 4.8. Doesn't the `const` qualifier add boilerplate and make code harder to read?\n**Short answer**: No, it doesn't and it can be quite the opposite.\n\n**Long answer**: Let's pretend we need to write a method with the following\nconstraints:\n- It must take a slice of pointers to objects of type `Object` as argument `s`.\n- It must return all objects from an internal slice.\n- It must use the function `Dependency` that's exported from a third-party\n  package `thirdparty` and pass `s` to it.\n- The `thirdparty.Dependency` function doesn't specify whether or not it'll\n  mutate `s` in the documentation.\n- It **must not mutate** `s`, neither the slice nor the referenced objects!\n- It must ensure the internal slice **cannot be mutated** from the outside!\n- It must ensure, that the receiver **is not mutated** in any way!\n\nOur current approach would be copying because there's no other way to ensure\nimmutability.\n```go\n/* WITHOUT IMMUTABILITY */\n\nfunc (rec *T) OurMethod(s []*Object) [] *Object {\n\ts_copy := make([] *Object, len(s))\n\tfor i, item := range s {\n\t\t// Clone the items to get rid of aliasing\n\t\t// Copying an aliased slice wouldn't make any sense otherwise\n\t\ts_copy[i] = item.Clone()\n\t}\n\n\t// Pass a copy of \"s\" to third-party function to ensure it doesn't modify it\n\tthirdparty.Dependency(s_copy)\n\n\t// Now return a deep copy of the internal slice to prevent any mutations\n\tinternal_copy := make([] *Object, len(rec.internal))\n\tfor i, item := range rec.internal {\n\t\t// Clone to avoid aliasing\n\t\t// Copying an aliased slice wouldn't make any sense otherwise\n\t\tinternal_copy[i] = item.Clone()\n\t}\n\treturn internal_copy\n}\n```\n\nNow feel free to remove the comments and compare the above copy-bloated code\nwith the code protected by the `const` qualifier:\n\n```go\n/* WITH IMMUTABILITY */\n\nfunc (rec * const T) OurMethod(\n    s const [] * const Object,\n) const [] * const Object {\n\tthirdparty.Dependency(s) // s is safe\n\treturn rec.internal      // rec.internal is safe\n}\n```\n\nIf you don't like the rather verbose type definitions then consider using type\naliasing to shorten the code:\n\n```go\ntype ConstSlice = const [] * const Object\n\nfunc (rec * const T) OurMethod(s ConstSlice) ConstSlice {\n  thirdparty.Dependency(s)\n  return rec.internal\n}\n```\n\n### 4.9. Why do we need the distinction between immutable and mutable reference types?\nSimply put, the question is: *why do we have to write out the rather verbose\n`const * const Object` and `const [] const Object` instead of just\n`const *Object` and `const []Object` respectively?*\n\nThere are certain situations where mutable references to immutable types are\nnecessary such as when we want to describe a dynamic, interlinked graph data\nstructure where the nodes of the graph are immutable.\n\n```go\n// GraphNode represents a node with outbound and inbound connections.\n// Connections can be changed, but the underlying nodes will remain immutable\ntype GraphNode struct {\n\tinbound * const GraphNode\n\toutbound * const GraphNode\n}\n```\n\nContrary, there are other situations where we'd require immutable references to\nmutable objects, such as in the case of rather complex functions taking\nreferences to mutable graph nodes:\n\n```go\n// MutateGraphNode takes an immutable pointer to a mutable graph node,\n// The pointer needs to be immutable so that it behaves like an\n// immutable variable so we can't accidentally change it in the scope\n// of the function potentially messing up the whole calculation!\nfunc MutateGraphNode(ref const * GraphNode) {\n\t/*...*/\n\tref = \u0026GraphNode{} // Compile-time error\n\t/*...*/\n\tref.outbound = \u0026GraphNode{} // fine\n\tref.Mutation()              // fine\n}\n```\n\nWithout this distinction, the above code wouldn't be possible and we'd have to\ncompromise compile-time safety by **removing immutability** to solve similar\nproblems. Reference types like pointers, slices, and maps are just regular types\nand should be treated as such consistently without any special regulations.\n\n### 4.10. Why implicitly cast mutable to immutable types?\nIt's true that in Go, types are converted explicitly with interface types being\nthe only exception to this rule. But making the typecasting of mutable- to\nimmutable types explicit would break backward-compatibility (see [issue\n#14](https://github.com/romshark/Go-2-Proposal---Immutability/issues/14) for\nmore details) and also make the language rather verbose.\n\n### 4.11. Can't these problems be solved by a linter?\nImplementing immutable types with a linter would **not** solve the following\nproblems:\n- **Verbosity and Confusion**: A linter would work based on *type names* and\n  require explicit definitions of immutable alias types (including primitive\n  types) with some kind of a pre- or postfix like \"ImmutType**Const**\", which is\n  way too verbose and confusing compared to the `const` qualifier based\n  approach! Nobody will ever write code like this:\n\n  ```go\n  type ConstInt int\n  // ConstT is an immutable T\n  type ConstT T\n  // ConstPointerConstT is an immutable pointer to an immutable T\n  type ConstPointerConstT * ConstT\n  // ConstSliceConstT is an immutable slice of pointers to immutable Ts\n  type ConstSliceConstT [] * ConstT\n\n  func (r * ConstT) Method(\n    a ConstPointerConstT,\n    v ConstSliceConstT,\n  ) (\n    rv * ConstT,\n  ) {\n    var integer ConstInt = 42\n    /*...*/\n  }\n  ```\n\n  ...to achieve similar results as:\n  ```go\n  func (r * const T) Method(\n    a const * const T,\n    v const [] * const T,\n  ) (\n    rv * const T,\n  ) {\n\tvar integer int = 42\n    /*...*/\n  }\n  ```\n- **Cross-Package Consistency**: A linter wouldn't protect the code from\n  cross-package mutability issues! If any external code like a third-party\n  library doesn't support the *immutable type name convention* of the linter\n  then the immutability simply can't be checked on these parts of the program\n  code making the entire concept useless. The standard library will never be\n  written in such a style, but it's essential to all Go 1.x code.\n\n## 5. Other Proposals\n### 5.1. [proposal: spec: add read-only slices and maps as function arguments #20443](https://github.com/golang/go/issues/20443)\nThe proposed kind of immutability described in the document above doesn't solve\nthe mutable shared state problem caused by [pointer\naliasing](https://en.wikipedia.org/wiki/Pointer_aliasing) at all proposing only\nexceptional treatment of slices and maps passed as function arguments.\n\n#### 5.1.1. Disadvantages\n- **Inconsistent:** it introduces exceptional rules for map- and slice-type\n  arguments leading to eventual specification inconsistency.\n- **Doesn't solve the root problem:** it doesn't take mutable pointer types into\n  account which are prone to [pointer\n  aliasing](https://en.wikipedia.org/wiki/Pointer_aliasing) leading to mutable\n  shared state just like slices and maps.\n- **Very limited:** it doesn't propose immutability for variables, methods,\n  fields, return values and arguments of any other type than slices and maps.\n- **Leads to performance degradation:** it proposes shallow-copying of slices\n  and maps passed to function argument instead of actual compile-time\n  immutability errors (even though it mentions it).\n- **Unclear:** it doesn't clearly define how to handle slice aliasing.\n- **Unclear:** it also doesn't clearly define how to handle nested container\n  types.\n- **Very limited:** it won't allow different combinations of mutable and\n  immutable types (such as passing mutable references inside immutable slices\n  and similar).\n\n#### 5.1.2. Similarities\n- Similar `const` keyword overloading with the same argumentation with slight\n  differences (used as argument field qualifier rather than as argument type\n  qualifier).\n\n----\n\n### 5.2. [proposal: Go 2: read-only types #22876](https://github.com/golang/go/issues/22876)\nThe proposed `ro` qualifier described in the document above is similar to\ncurrent proposal but still has some significant differences.\n\n#### 5.2.1. Disadvantages\n- **Backward-incompatible:** the proposed feature requires\n  backward-incompatible language changes.\n- **Limiting and partially pointless:** _`ro` Transitivity_ describes the\n  inheritance of immutability by types referenced by immutable references, which\n  limits the ability of the developer to describe *immutable references to\n  mutable objects* and similar. This limitation will make developers avoid using\n  `ro` types alltogether and use unsafe mutable types instead when a mix between\n  mutable and immutable references is necessary. An immutable slice of mutable\n  slices: `const [] [] int` wouldn't be possible with `ro` transitivity and\n  would leave the developer no choice but turning it into a mutable slice of\n  mutable slices: `[][]int` making the entire concept of read-only types\n  partially pointless.\n- **Less advanced:** it doesn't propose immutable struct fields.\n\n#### 5.2.2. Differences\n- \"Immutability\" is called \"read-only type permissions\" while constants are\n  called \"immutables\".\n\n#### 5.2.3. Similarities\n- The proposed `ro` qualifier is part of the type definition just as the `const`\n  qualifier.\n- Proposes immutable return values, arguments, interfaces and receivers.\n- Describes very similar benefits.\n\n----\nCopyright © 2018 [Roman Sharkov](https://github.com/romshark)\n(\u003croman.scharkov@gmail.com\u003e)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fromshark%2FGo-1-2-Proposal---Immutability","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fromshark%2FGo-1-2-Proposal---Immutability","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fromshark%2FGo-1-2-Proposal---Immutability/lists"}