Variable initialization and declaration
Go and C++ are following the same track here. Variables initialization and declaration are very similar.
var test int
test = 1
var test int = 1
test := 1
Some more constrain could here be less error prone but more painful to write.
A classical error is variable shadowing this way:
import (
"fmt"
)
func main() {
variable := 1
if true {
variable := 3
fmt.Println(variable)
}
fmt.Println(variable)
}
C++ style is less error prone (for shadowing) auto a = x
is more visible than :=
compared to =
. The -Wshadow
g++ warning is a possibility. From my experience shadowing occurs really less when writing C++ than Go.
#include <iostream>
using namespace std;
int main()
{
auto variable = 1;
{
auto variable = 3;
cout << variable << endl;
}
cout << variable << endl;
return 0;
}
Go way looks nice but the taste is strange.
Not used variable
func main() {
test := 1
}
prog.go:5:2 test declared and not used
I am sometimes lazy with this and I am not the only one. For code maintainability it's just awesome.
It can be annoying for debug. If you remove some expression Go starts complaining of unused variable, then of unused import, etc.
import "fmt"
func main(){
test := "test"
/*
fmt.Println(test)
*/
}
prog.go:X:5: fmt imported and not used
The most easy way to avoid this is to wrap expression with if (false) {…} but it's messy.
Clang and GCC warn about the unused variables since ages. I would love to have GCC/Clang style (warn and -Werror for release).
I love that as it make my code cleaner. Some improvement would help especially for writing debug code.
Multiple return values
I discovered that in Lua and find it really interesting.
Here is an example :
package main
import "fmt"
func set() (x, y, z int) {
return 1, 2, 3
}
func main() {
x, y, z := set()
fmt.Println(x, y, z)
}
1 2 3
Note : When a function returns multiple variables not all are used it’s important to avoid not using the result that takes long to compute.
I wish I could add a compilation warning sent when I am ignoring the variable taking cpu time to be computed.
A C++ way to do this:
#include <iostream>
#include <tuple>
using namespace std;
auto set() {
return make_tuple(1,2,3);
}
int main()
{
auto [a, b, c] = set();
std::cout << a << " " << b << " " << c << std::endl;
return 0;
}
I definitely like this. It's compacting code mostly for good.
Conditions If, else if
Go forces only one syntax:
if A {
} else {
}
Nested else if
in C++ style is not allowed :
if A {
If B {
} else if C {
}
}
At first go is destroying my brain coming from C++.
It felt like that :
- try raw code
- read doc
- code
- get annoyed
- read doc
- code
- getting the point
- be ok with go.
Forcing one syntax is a way to avoid bugs coming from mixing multiple syntax.
A kind of dictature but why not (in this, and only this, particular case).
It's easier to read code always written the same way even if I don't like loosing my freedom.
For loop
First thing I found simple is for, we tried to replace this but, we don’t have to reinvent the wheel.
For is extended as well which is good but C granny won’t get confused.
for i := 1; i<=4; i++ {
fmt.Println(i)
}
numbers := []int{1,2,3,4}
for _,v := range numbers {
fmt.Println(v)
}
Nothing really new here. C++ has a lot of patterns to do this including iterators which are not implemented (yet) in Go.
Switch
Switch in go and easy to read not far C++.
Go
switch t {
case 1:
fmt.Println("case 1")
default:
fmt.Println("default")
}
C++
switch (var) {
case 1:
cout << "case 1" << endl;
break;
default:
cout << "default" << endl;
}
String printing
Multiple ways of doing very close things, I got used quite fast but found it annoying.
import "fmt"
func main() {
str := "hello"
slice := []string{"one", "two", "three"}
fmt.Print(str)
fmt.Print("\n")
fmt.Print(slice)
fmt.Print("\n")
fmt.Println(str)
fmt.Println(slice)
fmt.Printf("%v\n", str)
fmt.Printf("%v\n", slice)
}
hello
[one two three]
hello
[one two three]
hello
[one two three]
It can really get messy and the Println("string")
function is a hook which can be replaced by Print("string\n")
. Note Println behave the same on all platforms, it does not change the separator to \r\n
in Windows. It's just syntactic sugar why not.
Where go it simpler than Cpp is to print slice content.
Cpp would do :
#include <iostream>
using namespace std;
int main()
{
auto list = {1, 2, 3};
for ( const auto &v: list) {
cout << v << " ";
}
cout << endl;
return 0;
}
After using Print and Println functions for more time it makes life easier. Go is often used in various containers having a simple and compact way to print objects and values is nice.
What is println ?
I just end up mistyping fmt.Println
to println
:
import "fmt"
func main() {
fmt.Println("Hello")
// ok
println("hello")
// ok
slice := []string{"toto", "tutu"}
fmt.Println(slice)
// ok
println(slice)
// ko
}
hello
hello
[toto tutu]
[2/2]0x1040a0d0
Interesting thing but what the hell is that, I guess it’s language internals ?
In fact it's just an helper for printf
debugging without additionnal import, it may disappear in the future.
This is just a something strange with no real impact (just never commit println).
More details here.
Bytes vs Rune
Rune is unicode character.
import "fmt"
import "reflect"
func main() {
str := "s"
str_utf8 := "漢"
rune_utf8 := []rune("漢")
//printing the object the first item and the first item type for each case
fmt.Println(str, string(str[0]), reflect.TypeOf(str[0]))
fmt.Println(str_utf8 , string(str_utf8[0]), reflect.TypeOf(str_utf8[0]))
fmt.Println(rune_utf8 , string(rune_utf8[0]), reflect.TypeOf(rune_utf8[0]))
}
s s uint8
漢 æ uint8
[28450] 漢 int32
It's a nice thing to have rune, but why not make it the default. Most of the time strings are to be shown. Unicode then makes sense. I feel like it's a good start stopped in the middle.
Conclusion
Go is growing a lot today and it's syntax is very easy to handle. For the basics it has some nice things.
Next part will be about objects and concurrency/parallelism in Go coming from C++ there Go and C++ differences have more impact.
And thanks to Dmytro who had a look at my post.
Top comments (2)
I’d just like to point out that, concerning the multiple value return, since C++17 you can now write
auto [a, b, c] = set();
which declaresa
,b
andc
as local variables just like you did in your Go code. I personally find it a bit easier to read than using the returned tuple as is.I’m looking forward the next part!
Thank you for your edit, I updated the code and the sample accordingly !