📜 ⬆️ ⬇️

A look at Go through the eyes of a .NET developer. Week # 1

Hello!

My name is Lex and I am the leader of the " ITB " youtube channel. And I’m an experienced worker with 6 years experience. Recently, I had a desire to go beyond my core technology (C # /. NET), and to get to know the essence of the Blub paradox . I firmly decided that I would try myself in another language, and the choice by chance fell on Go.

In order to get knowledge structured, I enrolled in a course from mail.ru, which you can find here: https://www.coursera.org/learn/golang-webservices-1 . Actually about what I learned from the first week of training in this course, further and will be discussed. Let's go!
')

I'll start with a little intro for the language. Go was developed in the days of multi-core processors (2007-2009), so with parallelization of work on the cores everything is very good here. In addition, the language works fine with competitive (simultaneous) requests. In general, a godsend for all sorts of web services and loaded web systems. Just what you need for me, for the last years I have been developing web eipia (Web API).

Start


To work with the language, it is enough to install the software package “Go tools” with the size of 118mb, and you can start coding.

The scripts have the * .go extension and are run by the go command on the command line (I am an adherent of the windows command line). The gofmt command arranges all spaces and makes the file candy (code beautifier out of the box). As in my favorite dot, in Go, the execution of a program begins with the main method.
Immediately struck a cool feature - you can not put a semicolon at the end of the line :)

Go is another language (along with python, c #, php, js, ts and others), which is convenient to work with in VSCode. VSCode itself delivers absolutely all the necessary packages and dependencies in the course of writing code. When you save the IDE, it kindly launches gofmt for you and makes the code even more beautiful.

Variables


Everything is familiar here: there are many different types, there are default values. Of the unusual for a dongle, the var keyword indicates what the variable will go next, and the type of the variable can be declared or not by the name of the variable. Unusually.

In general, there are many ways to declare and initialize variables. You can even assign values ​​to several variables separated by a comma in one line. And by the way, there are no implicit ghosts.

And in Go there is a " : = " operator that allows you to declare and immediately initialize new (and almost always only new) variables. Hello Pascal;)

Record type:

perem1, perem2 := 2, 3 
either creates two new variables, or creates one of them, and the second simply assigns a new value. This is me to the fact that if both variables already exist, then this does not work and you get an error, they say, use the usual assignment. Operator : = must create something :)

And then there is a funny fake - the underscore "_". It indicates the absence of a variable, something like this :) (in sharps, as it turned out, it also exists: https://docs.microsoft.com/en-us/dotnet/csharp/discards )

image

A funny moment with strings: the standard length string count len (str) counts bytes , and in UTF8, the character can weigh more. To count the characters, called runes , you need to use the method utf8.RuneCountInString (str) from the package "utf8". And, and more - the lines are not changed (just like in your favorite dotnet).

Constants


Constants can be declared bundles. Constants have an interesting either type or something - iota . I will call it Yopta. Yopta allows you to make some constants based on others, something like an iterator for constants (something reminded of the yield from the dotnet).

And yet, constants may not be typed. For such constants, the compiler itself decides what type they are at the right moment. Conveniently, what is there.



Pointers


Yes Yes! There are pointers. But it seems not very scary. It is said that they are not numbered, and it is impossible to add to the index of the even one, get the next pointer. Here it seems to be a variable that stores a reference to another variable (as long as I understand it somehow).

A pointer is specified via an ampersant or the new (type) method (to get a pointer to a type, not a variable):



If you refer directly to the pointer, the link changes. And if through the operator " * ", then the value of the variable that lies behind the pointer (to which it points) changes. In general, it is not very clear, but then, they say, it will be clearer.

Arrays


The size of the array is part of the data type! So, arrays of different dimensions are in fact different data types and are not compatible.



But nafig need arrays, if they are so dry and unchangeable (they can not be changed in runtime). Therefore, they invented such slices (slices) based on arrays.



The slice has a length (length) and a capacity (capacity) . It reminded me a bit of the nvarchar data type from SQL. Those. You can allocate space in memory (memory allocation) for your arrays, but put arrays of any length (up to capacity) there. In short, an interesting thing, you need to get used to it. For convenient use of slices, there are all sorts of methods like make (), append (), len (), cap () and, for sure, others. From one slice (slice) you can slightly get a cut (slice slice). And by the way, append () expands the capacity of a slice.

If the sub-cut is equal to the slice, then in fact they will refer to one memory location. And accordingly, a change in the value in one slice will lead to a change in the second slice. But if one of them is expanded via append () , then a new memory will be allocated.
In short, with the memory here everything is serious :)

Map, hash table, associative array


All this one and the same. Go has hash tables for quick key searches. Initialized as follows:



You can make subtables (mapy map), etc. (any level of nesting, sort of like). There is a default value that is returned for non-existent keys. It is taken from the key type (for bool, it is false). To know if the variable returned by default, i.e. that the key is missing - use the sign of the existence of the key (interesting thing):



Control structures


There is an if-else. Slightly more interesting than with sharps, because you can use initialization conditions for if.

There is a swith-case. Breaks do not need to put, and this is the difference. Inversion of logic) If necessary, so that the following condition is also checked - you need to write fallthrough .

There are for. And all with cycles.



Iterating over slices is more fun (range operator):



And even the hash map can be iterated (although the sequence will often be different, because the map is not directed):



For string type, it iterates over runes, not bytes.

Functions


Declared through the func keyword. And again, the inversion of logic compared to the dotnet - first enter the input parameters, and then the type of the return value (you can immediately named).

Functions can return multiple results , just like the tuples in a datnet .

Named values ​​returned by a function are, by default, initialized with values ​​by default for the type.

And also, you can make variable functions that have an unlimited number of similar input parameters (like param in data). And then a specific type slice comes to the input of the function.

Functions can have names, and can be and without names - anonymous . And they can be called, can be assigned to variables. It looks like javascript. You can even make types based on functions! Functions can be passed as parameters (read delegates from datnet)

There is even a closure (hell, awful)! You can reach the variables outside the function from the function (read, in the parent function). These are the pies.

You can declare a deferred execution of functions . Through the defer keyword this is done. Those. deferred functions are executed at the end of the function in which they are declared, in the reverse order of declaring these deferred functions. Here it is. Moreover, the initialization of the deferred function arguments occurs when the defer block is declared . This is important, especially if there is another function as an argument - it will be executed much earlier than you expect!

PANIC! Go has this function - panic () . It's like throwing in dotnet, but worse. This function stops the execution of the program. But this same panic can be handled by defer, as it is executed anyway at the end of the function, even after panic. And more - a panic, it is not try-catch. This is worse!

Structures (near-objects)


It is said that Go is not exactly an OOP-paradigm language. But there are such things as structures . If you are from the world of datnet, then you know that we also have structures - these are displays of objects that can be stored on the stack (types of values). In Go, a structure is like the only way to do something like an object. I can’t talk about value types yet, but the nature of interaction with structures is very similar to that of.

Each structure is essentially a separate type and can have a set of properties of specific types (including the type of another structure or type of function):



In addition, there is some mechanism of direct object inheritance from the data point. In the picture above, the Account structure is nested with the Person structure. This means that all the fields from Person will be available in a variable of type Account . If the property names coincide (in the example above, Id, Name ) there will be no conflict, but the value of the higher field will be taken:



Methods


Yes, there is not only the similarity of objects, but also methods. Almost OOP :) A method is a function tied to a specific type (for example, to a structure). The method differs slightly from the function by declaring: after the func keyword in parentheses, you need to specify the type to which this method belongs, and the role of passing a variable of this type. What is the role? If you put an asterisk in front of the type, then the variable will be passed by reference (we remember the pointers, it was there as well), and as a result, inside the method we will work with a specific type, not its copy.



From the picture above, we can conclude that UpdateName does not make sense, because it changes the copy of the structure, not the original. And this copy does not return. While SetName will change the original structure (thanks to the asterisk and transmission by reference).

Methods in structures are inherited (and according to the rules for inheriting properties), i.e. parent structure has access to all methods nested in its structures.

Methods can be in other types, not only in structures. It may seem that this is similar to the extension methods from dotnet, but no. Methods in Go can only be created for local types, i.e. types that are declared in this package (about packages a little further).

Packages, scope, namespaces


Only now I realized that there are no NEIMSPEYS in Go! At all! I realized this when, after compilation, I got an error, saying that in the same folder I have two files with the main method. It turns out that at the compilation stage everything from daddy seems to be glued together in one canvas! The folder is in fact the namespace. Such magic, comrades :)

Here, by the way, what is said in the dock:



A note for yourself: to smoke a dock
And here's another little article on the topic: https://www.callicoder.com/golang-packages/#the-main-package-and-main-function

So, they say, the basic folder structure looks like:


bin - collected binaries
pkg - temporary object files
src sources
Packages name the name of the folder in which the package files are located. Yes, a package can have many files that are imported into each other.

Another interesting rule: functions, properties, variables and constants starting with a capital letter can be used outside the package , i.e. imported . Anything with a small letter is used only inside the package. Analogue of access modifiers from datnet.

The import keyword works within a file, not a package.

Interfaces


With interfaces here things are interesting. We do not need to inherit interfaces with our structures. It is enough that the structure that comes as an input parameter to a certain method that accepts an interface type implements the methods of this interface. And there are no problems. Those. interface methods need not be implemented by a structure. But the structure must contain the implementation of all methods that are required in a particular function, where the interface type is used as an input parameter.

It turns out that, in contrast to a datnet, in Go an interface is a characteristic of the function in which it is used, and not a characteristic of a specific structure (object).

If it is even simpler, then the logic is this: no need to be able to quack to be a duck. If you are able to quack - then most likely you are a duck :)
And by the way, if your structure implements all the methods of an interface, then this structure can be assigned as values ​​of a variable of the implemented interface . Something like this :)

Type Switch-Case


In Go, there is a special switch-case that can work depending on the incoming type. Cool stuff:



By the way, this is how you can convert one type to another (for example, an interface type to a structure type, to get structure fields that are not accessible from the interface):



ok - a boolean indicates that the conversion was successful. Signs we have already encountered :)

The blank interface is the beast in the Go world. It can take any type at all. Something like dynamic or Object in datnet. For example, it is used in fmt.Println () and similar functions that use an empty interface in the implementation.

Interfaces can be built into each other, and thus make the composition of interfaces (what is said in the letter I (interface segregation) of the principles of SOLID )

Tests


In Go, all functions of tests begin with the word Test , and the test testing module parameter is taken as input. Files are named after the file being tested + the word _test ( main_test.go - tests for the main.go file). And the tests and test files are in the same package!



Epilogue


That's all! Thank you for your attention, and ready to discuss questions in the comments. See you in the following notes from my classes!

PS All my code walks for this week of training you can look at github

Source: https://habr.com/ru/post/455830/


All Articles