-
Notifications
You must be signed in to change notification settings - Fork 4
Update README.md and some questions/comments #3
base: master
Are you sure you want to change the base?
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -76,6 +76,9 @@ versions of the Go 1.x programming language will continue to compile and work as | |
expected. | ||
|
||
### 2.1. Immutable Fields | ||
|
||
*onokonem: do we really need the immutable fields in mutable struct? what for?* | ||
|
||
Immutable struct fields are declared using the `const` qualifier. Immutable | ||
fields can only be set during the definition of the object and are then | ||
immutable for the entire lifetime of the object within any context. | ||
|
@@ -114,6 +117,9 @@ func main() { | |
|
||
---- | ||
### 2.2. Immutable Methods | ||
|
||
*onokonem: should we rename this one to immutable receivers?* | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not quite sure yet. By saying I also stated, that, yes, technically this should be called "immutable receivers", though when explaining why we can execute a function with an immutable receiver on an immutable argument, speaking of "immutable methods" might be more intuitive and thus easier to understand. |
||
|
||
Immutable methods are declared using the `const` qualifier on the function | ||
receiver and guarantee to not mutate the receiver in any way when called. | ||
They can safely be used in immutable contexts, such as within other | ||
|
@@ -137,8 +143,8 @@ func (o *Object) MutatingMethod() const *Object { | |
// It's illegal to mutate any fields of the receiver. | ||
// It's illegal to call mutating methods of the receiver | ||
func (o const *Object) ImmutableMethod() const *Object { | ||
o.MutatingMethod() // Compile-time method | ||
o.mutableField = &Object{} // Compile-time method | ||
o.MutatingMethod() // Compile-time error | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Whoops, that's right. |
||
o.mutableField = &Object{} // Compile-time error | ||
return o.mutableField | ||
} | ||
|
||
|
@@ -197,6 +203,9 @@ func ReadObj( | |
|
||
---- | ||
### 2.4. Immutable Return Values | ||
|
||
*onokonem: do we really need the immutable fields in mutable struct? what for?* | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wait, this is about immutable fields, not about immutable return values, right? I suppose it's a mistake. Though let me clarify why we'd need immutable return values anyway. type Engine struct {}
// Shutdown is a non-const method
func (e *Engine) Shutdown() {
// Initiate engine shutdown
// BLock until engine is shut down
}
// ReadTemperature is a const-method
func (e const *Engine) ReadTemperature() (int, error) {
// Read the temperature, do tricky stuff
return 42, nil
}
type Car struct {
engine *Engine
}
// GetEngine is a const-method, it won't mutate the engine
func (c const *Car) GetEngine() const *Engine {
// Return immutable reference to the engine
return c.engine
}
func main() {
car := Car{
engine: &Engine{},
}
engine := car.GetEngine()
// GetEngine returns a read-only reference
// we can't stop the engine this way, because we're not allowed to for a reason!
engine.Shutdown() // Compile-time error
// Reading the temperature though is just fine, because it's a const method
temperature, _ := engine.ReadTemperature()
} The above code represents a case, where we want to return an immutable reference to a mutable internal field. Without immutable return values this wouldn't be possible. It's not the best example, but the concept should be clear. |
||
|
||
Immutable return values are declared using the `const` qualifier and guarantee | ||
that the returned objects will be immutable in any receiving context. | ||
|
||
|
@@ -362,4 +371,4 @@ considered of higher priority compared to other previously mentioned topics. | |
|
||
---- | ||
Copyright © 2018 [Roman Sharkov](https://github.com/romshark) | ||
(<[email protected]>) | ||
(<[email protected]>) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Immutable fields are useful in those cases when we want to ensure, that certain fields don't change during the entire lifetime of an object once they've been set by the constructor during the initialization.
Imagine that we'd want to implement a
Factory
type, that has aCreate() UniqueObject
method that creates unique object instances. Each object would, therefore, be supplied with a unique identifier:type UniqueObject struct { Ident string }
.In Go 1.11 we must make the identifier private and write a getter function
Identifier() string
to prevent the identifier from being changed during its lifetime.Though what we essentially want to achieve here is actually an immutable field.
Not only do we want to ensure, that
UniqueObject.Identifier
isn't mutated from the outside - we also want to prevent it from being mutated inside private methods and the package scope, that's why we'd want to have immutable struct fields.Also consider this: Why are we writing "dumb" getter methods in Go 1.x? ...Exactly! Because we can't just declare an exported but immutable field, so we have to emulate it using an unexported mutable field, and a method, that makes the verbal, insidious promise to not change it just returning a copy to the outside! It's insidious because the compiler doesn't guarantee that we (our colleagues, or the guys pushing their pull request on your github repo) can't mutate your internal field in the scope of our package! If we do - we wouldn't even know, and that's dangerous!
Conclusion: when you declare a new struct type, you always know intuitively what should be exported and what shouldn't. Same principles apply to mutability, you almost always know what's never going to change during the entire life-time of your object, so why not ensure, with a
const
constraint, that it's not changed by anyone anywhere anyway?P.S. I should add this to the main proposal document to clarify, why we really need immutable fields.