no-op/explicitly do nothing in go - go

I have a type method that mutates the type's fields. It takes no arguments and returns nothing. The bulk of the method is a switch block. I want to be able to "short-circuit" out of the switch block with a no-op. Before I refactored it into a type method, I would've just returned out of the function, but that's out. Removing the case would break the logic of the method--the default case mutates state, which I don't want to do if this case is matched. I need the equivalent of Python's pass, basically.
Code:
func (parser *Parser) endSectionName () {
state = parser.State
buffer = parser.buffer
results = parser.results
switch {
case state.HasFlag(IN_ESCAPED) {
// ???
}
case !inSection(state) {
return state, NotInSectionError
}
case !state.HasFlag(IN_SECTION_NAME) {
state.Reset()
return state, errors.New("Parsing error: Not in section name")
}
default {
state.RemoveFlag(IN_SECTION_NAME)
s := buffer.String()
results[s] = new(Section)
buffer.Reset()
return state, nil
}
}
}

Unlike in other languages, in Go the control flow breaks at each case of a switch statement, control doesn't flow into the next case unless it is explicitly "asked" for with the fallthrough statement.
And also a statement is not required after the case (it can be empty). See this example:
i := 3
switch i {
case 3:
case 0:
fmt.Println("Hello, playground")
}
It will print nothing even though i==3 and there is no statement after case 3.
Same as this:
i := 3
switch {
case i == 3:
case i == 0:
fmt.Println("Hello, playground")
}
Try it on the Go Playground.

Related

why do people use inner function in golang?

I am reading some open source go project and found there are many code implemented as below:
for id, s := range subscribers {
go func(id string, s *helloSaidSubscriber) {
select {
case <-s.stop:
unsubscribe <- id
return
default:
}
select {
case <-s.stop:
unsubscribe <- id
case s.events <- e:
case <-time.After(time.Second):
}
}(id, s)
}
in above code, the inner function go func...(id, s) looks like unnecessary. In other words, what the different if I write code like below:
for id, s := range subscribers {
select {
case <-s.stop:
unsubscribe <- id
return
default:
}
select {
case <-s.stop:
unsubscribe <- id
case s.events <- e:
case <-time.After(time.Second):
}
}
In your first example, that is an anonymous function with the go keyword causing it to function as a goroutine, which is a concurrency pattern in Go. So the code inside the anonymous function (the goroutine) will all process concurrently.
In your second example, excluding the goroutine means the code will run sequentially.
An anonymous function is a function which doesn’t contain any name. It is often used when you want to create an inline function. It can form a closure. An anonymous function is also known as a function literal.

Is it a good practice to lock some data outside if-else and unlock inside if-else

When I test rf.state == candidate, there could be a race. So I lock the struct rf before that. I want to make sure rf.state appears exactly in the if condition test so I can use its most current value.
...
func (rf *Raft) runElection() {
rf.mu.Lock()
rf.currentTerm += 1
rf.votedFor = rf.me
rf.votes = 1
rf.mu.Unlock()
for server := range rf.peers {
rf.mu.Lock()
if server != rf.me && rf.state == candidate {
rf.mu.Unlock()
go rf.sendRequestVote(server)
} else {
rf.mu.Unlock()
}
}
}
...
Is there any other idiomatic way to lock the data in similar situations (if-else, switch...)?
EDITS:
Here is some other code of mine.
...
rf.mu.Lock()
switch rf.state {
case follower:
rf.mu.Unlock()
select {
case <-rf.chanAppends:
case <-rf.chanGrantVotes:
case <-time.After(time.Duration(500+rand.Intn(1000)) * time.Millisecond):
rf.mu.Lock()
rf.state = candidate
rf.mu.Unlock()
}
case candidate:
rf.mu.Unlock()
...
case leader:
rf.mu.Unlock()
...
To use the current rf.state upon switch, I have to lock at the outside and unlock it at the inside. What bothers me is that for every case I have to unlock for every cases (making the code less clean and readable?). And there is some blocking (e.g. time.After(...)) going on to make a timer inside every case so I see no way of using defer to help me. I guess it's some tradeoff I have to make if I don't want to alter the logical structure of this switch?
While your code looks okay, you can always extract parts into their own functions to make use of defer.
func (rf *Raft) runElection() {
// ...
for server := range rf.peers {
tryVote(rf, server)
}
}
func tryVote(rf *Raft, server xxx) {
rf.mu.Lock()
defer rf.mu.Unlock()
if server != rf.me && rf.state == candidate {
go rf.sendRequestVote(server)
}
}
This is especially helpful when your code gets more complex and you want to make ABSOLUTELY sure the mutex gets unlocked once you return from some code section.

How to avoid non reachable code with switch-case?

We have below code scenario:
func f(a, b, c *int) *int {
check := c == nil
switch check {
case true:
if g(a) {
return nil
}
return h(a)
case false:
return k(a, b, c)
}
return nil // non reachable code
}
return nil is non-reachable code. Reproducible code: https://play.golang.org/p/lIrTxZkNbg6
Why Go compiler complains about non-reachable code? How to avoid non-reachable code? Is switch-case syntax causing non-reachable code?
Your switch statement is evaluating a boolean which has just 2 possible choices, so there is no possibility for it to ever reach the final return statement.
In this specific case it is more readable to use if instead of switch
if check {
if g(a) {
return nil
}
return h(a)
}
return k(a, b, c)
rewrite your function in more simple terms (using if).
func f(a, b, c *int) *int {
if c == nil {
if g(a) {
return nil
}
return h(a)
}
return k(a, b, c)
}
The compiler only knows that the statement is unreachable if it follows a terminating statement. Terminating switch statements are:
A "switch" statement in which:
there are no "break" statements referring to the "switch" statement,
there is a default case, and
the statement lists in each case, including the default, end in a terminating statement, or a possibly labeled "fallthrough" statement.
So, the rules aren't comprehensive enough to determine this case where every possible value of the switched data type has a terminating case (as you have here). This rule would add needless complexity to the compiler and language as, practically, it would only apply to the exact situation you've shown here, which can (and should) easily be replaced by if/else statements.
Keep in mind that case values in a switch statement don't necessarily need to be constant or unique, and bool is arguably the only data type that has few enough unique values that you might realistically cover all of them in a switch statement.

What does *(*int)(nil) = 0 mean in golang?

I notice there is one line *(*int)(nil) = 0 in function throw
//go:nosplit
func throw(s string) {
// Everything throw does should be recursively nosplit so it
// can be called even when it's unsafe to grow the stack.
systemstack(func() {
print("fatal error: ", s, "\n")
})
gp := getg()
if gp.m.throwing == 0 {
gp.m.throwing = 1
}
fatalthrow()
*(*int)(nil) = 0 // not reached
}
What does *(*int)(nil) = 0 means? and since this line *(*int)(nil) = 0 could NOT be reached, why it is here? any special usage?
The line:
*(*int)(nil) = 0
Tries to dereference a nil pointer and assign a value to it, which is always a runtime panic. The code should not reach this line, ever, but if it would anyhow (e.g. a faulty code change in the future), it would panic so the error could be detected and wouldn't go unnoticed.
It's common sense to do something similar in your code too, but with a more obvious "construct" such as panic("unreachable"). For example:
func sign(a int) string {
switch {
case a > 0:
return "Positive"
case a < 0:
return "Negative"
case a == 0:
return "Zero"
default:
panic("unreachable")
}
}
Note that in this example this is not just to detect errors early, it's also a requirement because to the compiler there would be no guarantee that a return statement would be reached. You could also move the panic("unreachable") statement after the switch (instead of the default branch), it's a matter of taste.
If you would change the above function to not return but print the sign, it would still be a good practice to leave the default branch to panic, although it's not a requirement in this variant:
func printSign(a int) {
switch {
case a > 0:
fmt.Println("Positive")
case a < 0:
fmt.Println("Negative")
case a == 0:
fmt.Println("Zero")
default:
panic("unreachable")
}
}

Should true or false terminate callback iteration?

In some languages it's necessary or cleaner to do iteration by providing a callback function that receives items and returns a boolean that indicates whether to continue or stop the iteration.
Which is the preferred value to indicate desire to stop/continue? Why? What precedents exist?
Example in Go:
func IntSliceEach(sl []int, cb func(i int) (more bool)) (all bool) {
for _, i := range sl {
if !cb(i) {
return false
}
}
return true
}
Which is the preferred value to indicate desire to stop/continue?
true for continue
Why?
Example 1:
func example(i interface{}) {
if w, ok := i.(io.Writer); ok {
// do something with your writer, ok indicates that you can continue
}
}
Example 2:
var sum int = 0
it := NewIntStatefulIterator(int_data)
for it.Next() {
sum += it.Value()
}
In both cases true (ok) indicates that you should continue. So I assume that it would be way to go in your example.
Foreword: The following answer applies to a callback function which decides based on the current item(s) whether the loop should terminate early - this is what you asked.
This is not to be confused with a function that progresses and reports if there are more elements to process, where a true return value is generally accepted to signal that there are more elements (for which a good example is Scanner.Scan()), and whose typical use is:
scanner := bufio.NewScanner(input)
for scanner.Scan() {
// Process current item (line):
line := scanner.Text()
fmt.Println(line) // Do something with line
}
Sticking to bool return type
Usually returning true to indicate termination results in code that is easier to read. This is due to the nature of for: if you do nothing, for continues, so you have to explicitly break if you want to terminate early, so having a clean termination condition is more common.
But it's a matter of taste. You may go whichever you like, but what's important is to name your callback function in a meaningful way that will clearly state what its return value means, and thus looking at the code (the condition in which it is used) will be easily understandable.
For example the following names are good and the return value is unambiguous:
// A return value of true means to terminate
func isLast(item Type) bool
func terminateAfter(item Type) bool
func abort(item Type) bool
// A return value of true means to continue (not to terminate)
func keepGoing(item Type) bool
func carryOn(item Type) bool
func processMore(item Type) bool
Using these results in easily understandable code:
for i, v := range vals {
doSomeWork()
if terminateAfter(v) {
break // or return
}
}
for i, v := range vals {
doSomeWork()
if !keepGoing(v) {
break // or return
}
}
// Or an alternative to the last one (subjective which is easier to read):
for i, v := range vals {
doSomeWork()
if keepGoing(v) {
continue
}
break
}
As negative examples, the following callback function names are bad as you can't guess what their return value mean:
// Bad: you can't tell what return value of true means just by its name:
func test(item Type) bool
func check(item Type) bool
Having error return type
It's also common for the callback to not just test but also do some work with the passed item. In these cases it is meaningful to return an error instead of a bool. Doing so, obviously the nil return value indicates success (and to continue), and a non-nil value indicates error and that processing should stop.
func process(item Type) error
for i, v := range vals {
if err := process(v); err != nil {
// Handle error and terminate
break
}
}
Having enum-like return value
Also if multiple return values have meaning, you may choose to define constants for return values, which you can name meaningfully.
type Action int
const (
ActionContinue Action = iota
ActionTerminate
ActionSkip
)
func actionToTake(item Type) Action
for i, v := range vals {
switch actionToTake(v) {
case ActionSkip:
continue
case ActionTerminate:
return
}
doSomeWork()
}

Resources