Wednesday 21 November 2018

Go Puzzle Solutions

Here are the answers to last months puzzles.

[Ed (2023/07/01): Some of these puzzles have more detailed explanations with diagrams in this slide deck, which was created for June 2023 Sydney Go Meetup, and contain some new puzzles.]

Please tell me if you find any mistakes.

1. c 
  
Maps are really just pointers (some people call maps reference types), so m and mcopy refer to the same map.  Since the element with an empty string key has been assigned to twice there are only 3 elements in the map. 
  
2. d 
  
The inbuilt copy function does not change the length of the destination slice; it just copies until the end of the source or destination.  In this case the source is shorter and so the other bytes (and the length) of s are not changed. 
  
3. c 
  
The first panic (line 8) is recovered at the first recover() encountered (line 5) which has its return value printed (ABC). Then another panic (line 6) occurs and is recovered by the nested defer (line 2) which has its return value printed (DEF). 
  
4. b 
  
The outer-scope variable c is captured in the closure passed to IndexFunc(). This contains 'c' which is matched at offset 2 in the string.  Hence IndexFunc() returns 2. 
  
5. c 
  
The string is composed of two runes of 3 bytes each.  The length of the string is the number of bytes as is the length of the slice of bytes when cast. 
  
6. a 
  
The init() functions are executed in the order they are declared so 1 then 2 are written to the channel.  The writes do not block because the channel has a buffer size of 2. The main() function reads and prints the first value sent.
  
7. c 
  
The iota has the value zero in the first line and is incremented for every subsequent line until the end of the const.  Hence the total would be 0+1+2+3 = 6. 
  
8. d 
  
The slice s has length of one (2-1) and starts at index one of the array.  The copy() statement only copies to the minimum of the source and destination so only overwrites the array element at index one.  Hence the array element at index 2 (43) is unchanged. 
  
9. d 
  
The order that a map is iterated in a for-range loop is unspecified. If element 1 is seen first the first time through the loop it will be deleted and "1 1 " will be printed, then the 2nd time through the loop "2 1 " will be printed (b).  If element 2 is seen first then it will not be deleted until the 2nd time through the loop hence "2 2 1 1 " will be printed (c). 
  
10. b 

The 3 value slicing is rarely used but allows you to specify the offset of the capacity of the new slice.  So b has 
a length of 2 (2-0) and a capacity of 4 (4-0).  The difference between capacity and length is 2.

If the re-slice did not specify a capacity (a[0:2]) then it the slice would have a capacity of the underlying array and the answer would be c (5 - 2 = 3).
  
11. b 
  
This is my favourite trick question (most people think d is the answer).  There is a subtle bug that the sync.WaitGroup is passed to the closure by value.  Hence the Wait() method will never return, and the main() function will just print "B" and exit.

You should never pass a WaitGroup by value - either capture it within the closure or pass a pointer to it.  Note that "go vet" will warn you of this problem with this message: func passes lock by value: sync.WaitGroup contains sync.noCopy.
  
If the defect is fixed then the answer is d, since which go-routine proceeds after Done() is executed and Wait() can proceed is indeterminate.  It might still just print "B" is the main() function exits and the program stops before the other go-routine has a chance to print "A". 
  
12. b
  
Numeric literals have no exact type so 2e2, for example, can be assigned to an int without a cast.  However, they have a "prefered" type which is the type they take given no constraints.  Hence i has type int whereas f has type float64. 
  
In the 2nd line the 2nd expression is constrained to be of type float64 because of the type of f.  Similarly, the 3rd expression is of type int because of the type of i. 
  
The literals in the first expression in the 2nd line are unconstrained but an int (preferred type of 200) is trumped by float64 (the preferred type of 2e2). 
  
13. a
  
The append requires a memory allocation since the new slice has a length of 4 (length of s plus 3), but the capacity of s is only 2.  The new slice t has length 4 and capacity of at least 4, but s is unchanged in length (and value).

[UPDATE: Note that after an allocation the capacity of the new slice is unspecified, but at least 4, so answers of "1 5", "1 6", etc are also possible.  However, the only possible answer of the choices given is a = "1 4"]
  
14. c 
  
The variable (a) is an array of 4 elements since the last index specified is 3 (idx+1). Note that the unspecified elements (with indices of 0 and 2) are initialised to zero. 
  
15. a  [UPDATE: previous incorrect answer was b]
  
Despite claims to the contrary interfaces are not "reference" types.  Any change to b cannot affect the int a.  The answers c and d can never happen since all parameters passed to fmt.Println() are automatically cast to the interface{} type and the referenced value is printed. 
  
16. c 
  
There is no initialiser on the 2nd and 4th lines so the one from the line above is used. By the 3rd line iota has the value 2 and on the 4th line it's 3. Hence the answer is 0+0+2+3 = 5. 
  
17. c 
  
The first append does not require a reallocation since the slice passed as the first parameter has length 1 but capacity 2 - so 3 is placed in the underlying array element at index 1. The second append requires a reallocation so the original underlying array is not touched. 
  
18. d 
  
The type of a map key must be a comparable type so it can't be a slice, map or function type, or a type (struct, array or interface) containing such a non-comparable type. Map keys can be interface types as long as the interface does not contain a non-comparable type.

In this case the underlying type for the key of the 2nd element added (m[b]) is a slice. This is not allowed since slices cannot be compared, but because the slice is inside an interface this cannot be determined at compile-time so instead causes a panic at run-time. 
  
19. c
  
Normally you can't modify a value returned from a map and have it reflected in the map since a copy of the map element is returned. However, in this case the map contains pointers. We don't modify the pointer but the int field of the struct pointed to is incremented from 1 to 2. 
  
20. c 
  
The closure (a) returns the year for the current time (2018 at time of writing). The call to Format() returns a string containing the current year as 2 digits (it returns"18" at time of writing) which is then converted to an int (18).  Hence the result is 18-2018 = -2000. 
  
21. d 
  
The fallthrough statement can only be used at the end of a case.  (If it compiled the answer would be a.) 
  
22. b
  
The value in a for-range loop is a copy of the slice element.  Incrementing it has no effect on the slice. 
  
23. a 
  
When you copy into a slice, no more elements than the original length of the slice are copied.  The capacity is irrelevant. 
  
24. d 
  
Whether the address of a zero-sized object is unique is explicitly not defined for the Go language. But typically this will print true or false. 
  
25. c 
  
When an interface type is used as a map key the underlying values are compared (type and value). In this case both underlying values are strings with the same value, so m[a] and m[b] refer to the same element. 
  
26. d 
  
In a type-switch the variable assigned from the interface value (in this case t) takes the type specified within the code for its case clause.  However, in this case there is a list of types (int and uint) so it cannot have a specific type and retains the interface type. (If separate case clauses were use for int and uint then the answer would be a.) 
  
27. b 
  
Casting an integer to a string creates a string with a single rune represented by the integer. In this case the Unicode value 65 is the same as the ASCII value - a capital A. 
  
28. d 
  
Array elements may be initialized by index in this way but in this case i1 and i3 have the same value.  The compile sensibly detects that the same element is being set twice and produces an error. 
  
29. a 
  
The type intA is a unique type so cannot be mixed in expressions with ints without a cast.  However, the type intB is just an alias for int so the expression int(a) | b is allowed.  Of course, the bit-wise OR operation turns of all bits (1 | 2 = 0). 
  
30. c 
  
There are no tricks here, except that you may not realise that you do not need to wait on the channel (the return value of ctx.Done()) before getting the result from ctx.Err(). 
  
31. a 
  
Iota is zero in the first line and one in the 2nd, hence the answer is 0+0+2+3 = 5. 
  
32. a [UPDATE: previous incorrect answer was d]

We are comparing two interfaces one of which contains a string and one of which contains a function.  You might expect this to panic since you cannot compare functions (see the answer to question 18 for more information).  However, when comparing interfaces the runtime appears to compare the types first and if different gives the values false if the types do not match (as in this case).

Note that I first tried this on Go 1.8 or 1.9 (Windows 64-bit) and believe I got a panic (answer d), but have since not been able to reproduce that behaviour (with any version of Go).  I am not sure if this was a bug in the Go runtime that was fixed or I was mistaken.
  
33. a 
  
The string to be printed contains 2 bytes containing the letter 'A' and a byte with the hex value BC. The %q format specifier does more than place double-quotes around the output string it also escapes characters making the string suitable for inserting into Go source code.  (If %s was used instead then c would be the answer.)