complete guide Archives - Welcome To Golang By Example https://vikasboss.github.io/tag/complete-guide/ Thu, 14 Jul 2022 16:47:18 +0000 en-US hourly 1 https://wordpress.org/?v=6.8.1 https://i0.wp.com/golangbyexamples.com/wp-content/uploads/2021/05/cropped-go_border-1.png?fit=32%2C32&ssl=1 complete guide Archives - Welcome To Golang By Example https://vikasboss.github.io/tag/complete-guide/ 32 32 159787465 Method in Go (Golang) https://vikasboss.github.io/method-in-golang/ https://vikasboss.github.io/method-in-golang/#comments Sat, 20 Jun 2020 06:08:11 +0000 https://vikasboss.github.io/?p=2289 This is the  chapter 20 of the golang comprehensive tutorial series. Refer to this link for other chapters of the series – Golang Comprehensive Tutorial Series Next Tutorial – InterfacePrevious Tutorial – Maps Now let’s check...

The post Method in Go (Golang) appeared first on Welcome To Golang By Example.

]]>
This is the  chapter 20 of the golang comprehensive tutorial series. Refer to this link for other chapters of the series – Golang Comprehensive Tutorial Series

Next Tutorial – Interface
Previous Tutorial – Maps

Now let’s check out the current tutorial. Below is the table of contents for current tutorial.

Overview

A method in golang is nothing but a function with a receiver. A receiver is an instance of some specific type such as struct, but it can be an instance of any other custom type. So basically when you attach a function to a type, then that function becomes a method for that type. The method will have access to the properties of the receiver and can call the receiver’s other methods.

Why Method

Since method lets you define a function on a type, it lets you write object-oriented code in Golang. There are also some other benefits such as two different methods can have the same name in the same package which is not possible with functions

Format of a Method

Below is the format for a method

func (receiver receiver_type) some_func_name(arguments) return_values

The method receiver and receiver type appear between the func keyword and the function name. The return_values come at the last.

Also, let’s understand more differences between a function and a method. There are some important differences between them. Below is the signature of a function

Function:

func some_func_name(arguments) return_values

We have already seen the signature of a method

Method:

func (receiver receiver_type) some_func_name(arguments) return_values

From the above signature, it is clear that the method has a receiver argument. This is the only difference between function and method, but due to it they differ in terms of functionality they offer

  • A function can be used as first-order objects and can be passed around while methods cannot.
  • Methods can be used for chaining on the receiver while function cannot be used for the same.
  • There can exist different methods with the same name with a different receiver, but there cannot exist two different functions with the same name in the same package.

Methods on Structs

Golang is not an object-oriented language. It doesn’t support type inheritance, but it does allow us to define methods on any custom type including structs. Since struct is a named collection of fields and methods can also be defined on it. As such struct in golang can be compared to a class in Object-Oriented Languages.

Let’s see an example of method on struct

package main

import "fmt"

type employee struct {
    name   string
    age    int
    salary int
}

func (e employee) details() {
    fmt.Printf("Name: %s\n", e.name)
    fmt.Printf("Age: %d\n", e.age)
}

func (e employee) getSalary() int {
    return e.salary
}

func main() {
    emp := employee{name: "Sam", age: 31, salary: 2000}
    emp.details()
    fmt.Printf("Salary %d\n", emp.getSalary())
}

Output

Name: Sam
Age: 31
Salary 2000

Notice that the receiver is available inside the method and fields of the receiver can be accessed inside the method.

Can field of the receiver also be changed inside the method?

Let’s see that

package main

import "fmt"

type employee struct {
    name   string
    age    int
    salary int
}

func (e employee) setNewName(newName string) {
    e.name = newName
}

func main() {
    emp := employee{name: "Sam", age: 31, salary: 2000}
    emp.setNewName("John")
    fmt.Printf("Name: %s\n", emp.name)
}

Output

Name: Sam

A method setNewName is defined on the employee struct in the above code. In this method, we update the name of the employee like this

e.name = newName

After setting the new name when we print the employee name again in the main function, we see that the old name “Sam” is printed instead of “John”. This happens because method is defined on a value receiver

func (e employee) setNewName(newName string)

Since the method is defined on a value receiver when the method is called a copy of the receiver is made and that copy of the receiver is available inside the method. Since it is a copy, any changes made to the value receiver is not visible to the caller. That is why it prints the old name “Sam” instead of “John”. Now the question which comes to the mind whether there is any way to fix this. And the answer is yes, and this is where pointer receivers come into the picture.

Method on a Pointer Receiver

In the above example we saw a method on a value receiver. Any change made to a value receiver is not visible to the caller. Methods can also be defined on a pointer receiver. Any change made to the pointer receiver will be visible to the caller. Let’s see an example

package main

import "fmt"

type employee struct {
    name   string
    age    int
    salary int
}

func (e *employee) setNewName(newName string) {
    e.name = newName
}

func main() {
    emp := &employee{name: "Sam", age: 31, salary: 2000}
    emp.setNewName("John")
    fmt.Printf("Name: %s\n", emp.name)
}

Output

Name: John

In above program, we defined the method setNewName on a pointer receiver

func (e *employee) setNewName(newName string)

Then we created an employee pointer and called the setNewName method on it. We see that the changes made to the employee pointer inside the setNewName are visible to the caller and it prints the new name.

Is it necessary to create the employee pointer to call a method with a pointer receiver? No, it is not. The method can be called on the employee instance and the language will take care of it to correctly pass it as a pointer to the method. This flexibility is provided by the language.

Let’s see an example

package main

import "fmt"

type employee struct {
    name   string
    age    int
    salary int
}

func (e *employee) setNewName(newName string) {
    e.name = newName
}

func main() {
    emp := employee{name: "Sam", age: 31, salary: 2000}
    emp.setNewName("John")

    fmt.Printf("Name: %s\n", emp.name)

    (&emp).setNewName("Mike")
    fmt.Printf("Name: %s\n", emp.name)
}

Output

Name: John
Name: Mike

We see in the above program that even if a method is defined on a pointer receiver but we are calling the method with a non-pointer employee instance

emp.setNewName("John")

But the language passes the receiver as a pointer and therefore the changes are visible to the caller.

Even this way of calling is valid

(&emp).setNewName("Mike")

Now, how about the other way around. If a method is defined on a value receiver, can the method be called with a pointer of the receiver?

Yes, even this is valid and the language takes care of passing the argument correctly as value receiver irrespective of whether the method was called on a pointer or normal struct.

Let’s see an example

package main

import "fmt"

type employee struct {
    name   string
    age    int
    salary int
}

func (e employee) setNewName(newName string) {
    e.name = newName
}

func main() {
    emp := employee{name: "Sam", age: 31, salary: 2000}
    emp.setNewName("John")

    fmt.Printf("Name: %s\n", emp.name)
    (&emp).setNewName("Mike")

    fmt.Printf("Name: %s\n", emp.name)
    emp2 := &employee{name: "Sam", age: 31, salary: 2000}
    emp2.setNewName("John")
    fmt.Printf("Name: %s\n", emp2.name)
}

Output

Name: Sam
Name: Sam
Name: Sam

Do note here since in all three cases, the setNewName method had a value receiver hence changes are not visible to the caller as the value is passed as a copy. It prints the old name in all three cases

To summarize what we learnt above

  • If a method has a value receiver it supports calling of that method with both value and pointer receiver
  • If a method has a pointer receiver then also it supports calling of that method with both value and pointer receiver

This is unlike function where if

  • If a function has a pointer argument then it will only accept a pointer as an argument
  • If a function has a value argument then it will only accept a value as an argument

When to use pointer receiver

  • When the changes to the receiver made inside the method have to be visible to the caller.
  • When the struct is big, then it is better to use a pointer receiver otherwise a copy of the struct will be made every time a method is called which will be expensive

Some More Points to note about methods

  • The receiver type has to be defined in the same package as the method definition. On defining a method on a receiver that exists in a different package, below error will be raised.
ERROR: cannot define new methods on non-local types
  • Till now we have seen a method invocation using a dot operator. There is one other way to call a method as well as shown in below example
package main

import "fmt"

type employee struct {
	name   string
	age    int
	salary int
}

func (e employee) details() {
	fmt.Printf("Name: %s\n", e.name)
	fmt.Printf("Age: %d\n", e.age)
}

func (e *employee) setName(newName string) {
	e.name = newName
}

func main() {
	emp := employee{name: "Sam", age: 31, salary: 2000}
	employee.details(emp)

	(*employee).setName(&emp, "John")

	fmt.Printf("Name: %s\n", emp.name)
}

Output

Name: Sam
Age: 31
Name: John

In above example we see a different method for calling a method. There are two cases

  • When the method has a value receiver then it can be called as below which is struct name followed by method name. The first argument is the value receiver itself.
employee.details(emp)
  • When the method has a pointer receiver then it can be called as below which is a pointer to struct name followed by method name. The first argument is the pointer receiver.
(*employee).setName(&emp, "John")

Also note that arguments of the method starts from the second argument as for setName function above:

(*employee).setName(&emp, "John")

You will rarely see this style being used and the dot notation style that we discussed earlier is the recommended as well as the most common way.

Methods on Anonymous nested struct fields

Let’s see a program

package main

import "fmt"

type employee struct {
	name   string
	age    int
	salary int
	address
}

type address struct {
	city    string
	country string
}

func (a address) details() {
	fmt.Printf("City: %s\n", a.city)
	fmt.Printf("Country: %s\n", a.country)
}

func main() {
	address := address{city: "London", country: "UK"}

	emp := employee{name: "Sam", age: 31, salary: 2000, address: address}

	emp.details()

	emp.address.details()
}

Output

City: London
Country: UK
City: London
Country: UK

Notice in above program that details method of address struct can be accessed in two ways

emp.details()
emp.address.details()

So in case of an anonymous nested struct, methods of that struct can be directly accessed.

Exported Method

Go doesn’t have any public,  private or protected keyword. The only mechanism to control the visibility outside the package is using the capitalized and non-capitalized formats

  • Capitalized Identifiers are exported. The capital letter indicates that this is an exported identifier and is available outside the package.
  • Non-capitalized identifiers are not exported. The lowercase indicates that the identifier is not exported and will only be accessed from within the same package.

So any struct which starts with a capital letter is exported to other packages.  Similarly, any struct field which starts with capital is exported otherwise not. And also similarly any struct method which starts with a capital letter is exported. Let’s see an example that shows exporting and non-exporting of structs, struct fields, and methods.  See model.go and test.go below. Both belong to the main package.

  • Structure
    • Struct Person is exported
    • Struct company is non-exported
  • Structure’s Field
    • Person struct field Name is exported
    • Person struct field age is not exported but Name is exported
  • Structure’s Method
    • Person Struct’s Method GetAge() is exported
    • Person Struct’s Method getName() is not exported

model.go

package main

import "fmt"

//Person struct
type Person struct {
    Name string
    age  int
}

//GetAge of person
func (p *Person) GetAge() int {
    return p.age
}

func (p *Person) getName() string {
    return p.Name
}

type company struct {
}

Let’s write a file test.go in same main package. See below.

test.go

package main

import "fmt"

//Test function
func Test() {
    //STRUCTURE IDENTIFIER
    p := &Person{
        Name: "test",
        age:  21,
    }
    fmt.Println(p)
    c := &company{}
    fmt.Println(c)
    
    //STRUCTURE'S FIELDS
    fmt.Println(p.Name)
    fmt.Println(p.age)

    
    //STRUCTURE'S METHOD
    fmt.Println(p.GetAge())
    fmt.Println(p.getName())

}

On running this file, it is able to access all exported and un-exported fields in model.go as both lies in the same package main. There is no compilation error and it gives below output

Output

&{test 21}
&{}
test
21
21
test

If we move the file model.go to a different package named model. Now the output on running ‘go build’ will give compilation errors. All the compilation errors are because test.go in main package to not able to refer to un-exported fields of model.go in model package.

The compilation error will be

p.getName undefined (cannot refer to unexported field or method model.(*Person).getName)

Method Chaining

For method chaining to be possible the methods in the chain should return the receiver. Returning the receiver for the last method inn the chain is optional.

Let’s see an example of method chaining.

package main

import "fmt"

type employee struct {
	name   string
	age    int
	salary int
}

func (e employee) printName() employee {
	fmt.Printf("Name: %s\n", e.name)
	return e
}

func (e employee) printAge() employee {
	fmt.Printf("Age: %d\n", e.age)
	return e
}

func (e employee) printSalary() {
	fmt.Printf("Salary: %d\n", e.salary)
}

func main() {
	emp := employee{name: "Sam", age: 31, salary: 2000}
	emp.printName().printAge().printSalary()
}

Output

Name: Sam
Age: 31
Salary: 2000

Methods on Non-Struct Types

Methods can also be defined on a non-struct custom type. Non-struct custom types can be created through type definition. Below is the format for creating a new custom type

type {type_name} {built_in_type}

For example we can a named custom type myFloat of type float64

type myFloat float64

Methods can be defined on the named custom type. See below example:

Code

package main

import (
    "fmt"
    "math"
)

type myFloat float64

func (m myFloat) ceil() float64 {
    return math.Ceil(float64(m))
}

func main() {
    num := myFloat(1.4)
    fmt.Println(num.ceil())
}

Output

2

Conclusion

This is all about using method in Go. Hope you have liked this article. Please share feedback/mistakes/improvements in comments

Next Tutorial – Interface
Previous Tutorial – Maps

The post Method in Go (Golang) appeared first on Welcome To Golang By Example.

]]>
https://vikasboss.github.io/method-in-golang/feed/ 1 2289
Struct in Go (Golang) https://vikasboss.github.io/struct-in-golang-complete-guide/ https://vikasboss.github.io/struct-in-golang-complete-guide/#comments Sun, 14 Jun 2020 09:01:20 +0000 https://vikasboss.github.io/?p=2244 This is the  chapter 16 of the golang comprehensive tutorial series. Refer to this link for other chapters of the series – Golang Comprehensive Tutorial Series Next Tutorial – ArrayPrevious Tutorial – Pointer Now let’s check...

The post Struct in Go (Golang) appeared first on Welcome To Golang By Example.

]]>
This is the  chapter 16 of the golang comprehensive tutorial series. Refer to this link for other chapters of the series – Golang Comprehensive Tutorial Series

Next Tutorial – Array
Previous Tutorial – Pointer

Now let’s check out the current tutorial. Below is the table of contents for current tutorial

Overview

GO struct is named collection of data fields which can be of different types. Struct acts as a container that has different heterogeneous data types which together represents an entity. For example, different attributes are used to represent an employee in an organization. Employee can have

  • Name of string type
  • Age of int type
  • DOB of time.Time type
  • Salary of int type

.. and so on. A struct can be used to represent an employee

type employee struct {
    name   string
    age    int
    salary int
}

A struct in golang can be compared to a class in Object Oriented Languages

Declaring a struct type

Below is the format for declaring a struct

type struct_name struct {
    field_name1 field_type1
    field_name2 field_type2
    ...
}

In the above format, struct_name is the name of the struct. It has a field named field_name1 of type field_type1 and a field named field_name2 of type field_type2. This declares a new named struct type which acts as a blueprint. The type keyword is used to introduce a new type

Example

type point struct {
    x float64
    y float64
}

The above declaration declares a new struct named point which has two field x and y. Both fields are of float64 type.Once a new struct type is declared we can define new concrete struct variable from it as we will see in next section

Creating a struct variable

Declaring a struct just declares a named struct type. Creating a struct variable creates an instance of that struct with memory being initialized as well. We can create a empty struct variable without given any value to any of the field

emp := employee{}

In this case, all the fields in the struct are initialized with a default zero value of that field type.

We can also initialize the value for each struct field while creating a struct variable. There are two variations

  • Each field on the same line
emp := employee{name: "Sam", age: 31, salary: 2000}
  • Each field on different lines
emp := employee{
   name:   "Sam",
   age:    31,
   salary: 2000,
}

It is also ok to initialize only some of the fields with value. The field which are not initialized with value will get the default zero value of their type

emp := employee{
   name: "Sam",
   age: 31,
}

In above case salary will get default value of zero since it is not initialized

Let’s see a working code illustrating above points:

package main

import "fmt"

type employee struct {
    name   string
    age    int
    salary int
}

func main() {
    emp1 := employee{}
    fmt.Printf("Emp1: %+v\n", emp1)

    emp2 := employee{name: "Sam", age: 31, salary: 2000}
    fmt.Printf("Emp2: %+v\n", emp2)

    emp3 := employee{
        name:   "Sam",
        age:    31,
        salary: 2000,
    }
    fmt.Printf("Emp3: %+v\n", emp3)

    emp4 := employee{
        name: "Sam",
        age:  31,
    }
    fmt.Printf("Emp4: %+v\n", emp4)
}

Output

Emp1: {name: age:0 salary:0}
Emp2: {name:Sam age:31 salary:2000}
Emp3: {name:Sam age:31 salary:2000}
Emp4: {name:Sam age:31 salary:0}

For above program

  • We first declare an employee struct.
  • emp1’s fields are all initialized with default zero value of its type i.e name with “”, age and salary with 0.
  • emp2 has been initialized with all fields on the same line. Its fields are correctly printed with their value
  • emp3’s has been initialized with all fields on different lines. Its fields are correctly printed with their value
  • emp4’s salary field is initialized with default zero value of 0. While other other two fields are correctly printed with their value.

It is to be noted that in the initialization of a struct, every new line with in curly braces has to end with a comma. So below initialization will raise error as

"salary" : 2000

doesn’t end with a comma.

emp := employee{
  name:   "Sam",
  age:    31,
  salary: 2000
}

This will be fine

emp := employee{
  name:   "Sam",
  age:    31,
  salary: 2000}

Without field names

struct can also be initialized without specifying the field names. But in this case, all values for each of the field has to be provided in sequence

emp := employee{"Sam", 31, 2000}

A compiler error will be raised if all values are not provided when field name is not used.

Let’s see a program

package main

import "fmt"

type employee struct {
    name   string
    age    int
    salary int
}

func main() {
    emp := employee{"Sam", 31, 2000}
    fmt.Printf("Emp: %+v\n", emp)

    //emp = employee{"Sam", 31}
}

Output

Emp2: {name:Sam age:31 salary:2000}

Uncomment the line

emp = employee{"Sam", 31}

in the above program, and it will raise compiler error

too few values in employee literal

Accessing and Setting Struct Fields

Structs fields can be accessed using the dot operator. Below is the format for getting the value

n := emp.name

Similarly a value can be assigned to a struct field too.

emp.name = "some_new_name"
package main

import "fmt"

type employee struct {
    name   string
    age    int
    salary int
}

func main() {
    emp := employee{name: "Sam", age: 31, salary: 2000}

    //Accessing a struct field
    n := emp.name
    fmt.Printf("Current name is: %s\n", n)

    //Assigning a new value
    emp.name = "John"
    fmt.Printf("New name is: %s\n", emp.name)
}

Output

Current name is: Sam
New name is: John

Pointer to a struct

There are two ways of creating a pointer to the struct

  • Using the & operator
  • Using the new keyword

Let’s looks at each of above method one by one.

Using the & operator

The & operator can be used to get the pointer to a struct variable.

emp := employee{name: "Sam", age: 31, salary: 2000}
empP := &emp

struct pointer can also be directly created as well

empP := &employee{name: "Sam", age: 31, salary: 2000}

Let’s look at a program

package main

import "fmt"

type employee struct {
    name   string
    age    int
    salary int
}

func main() {
    emp := employee{name: "Sam", age: 31, salary: 2000}
    empP := &emp
    fmt.Printf("Emp: %+v\n", empP)
    empP = &employee{name: "John", age: 30, salary: 3000}
    fmt.Printf("Emp: %+v\n", empP)
}

Output

Emp: &{name:Sam age:31 salary:2000}
Emp: &{name:John age:30 salary:3000}

Using the new keyword

Using the  new() keyword will:

  • Create the struct
  • Initialize all the field to the zero default value of their type
  • Return the pointer to the newly created struct

This will return a pointer

empP := new(employee)

Pointer address can be print using the %p format modifier

fmt.Printf("Emp Pointer: %p\n", empP)

Deference operator ‘*’ can be used to print the value at the pointer.

fmt.Printf("Emp Value: %+v\n", *empP)

It will print

Emp Value: {name: age:0 salary:0}

When not using the dereference pointer but using the format identifier  %+v, then ampersand will be appended before the struct indicating that is a pointer.

fmt.Printf("Emp Value: %+v\n", empP)

It will print

Emp Value: &{name: age:0 salary:0}

Let’s see full program denoting above points

package main

import "fmt"

type employee struct {
    name   string
    age    int
    salary int
}

func main() {
    empP := new(employee)
    fmt.Printf("Emp Pointer Address: %p\n", empP)
    fmt.Printf("Emp Pointer: %+v\n", empP)
    fmt.Printf("Emp Value: %+v\n", *empP)
}

Output

Emp Pointer Address: 0xc000130000
Emp Pointer: &{name: age:0 salary:0}
Emp Value: {name: age:0 salary:0}

Print a Struct Variable

There are two ways to print all struct variables including all its key and values.

  • Using the fmt package
  • Printing the struct in JSON form using the json/encoding package. This also allows pretty print of a struct as well.

Let’s see the two ways in which we can print the instance of the employee struct.

Using the fmt package

fmt.Printf() function can be used to print a struct.  Different format identifiers can be used to print a struct in different ways. Let’s see how different format identifiers can be used to print a struct in different formats.

Let’s first create an instance of employee

emp := employee{name: "Sam", age: 31, salary: 2000}
  • %v – It will print only values. Field name will not be printed. This is the default way of printing a struct. Eg
fmt.Printf("%v", emp)  -  {Sam 31 2000}
  • %+v – It will print both field and value. Eg
fmt.Printf("%+v", emp) - {name:Sam age:31 salary:2000}

fmt.Println() function can also be used to print a struct. Since %v is the default for fmt.Printlin() function, hence output will be same as using %v for fmt.Printf()

fmt.Println(emp) - {Sam 31 2000}

Let’s see a working program too

package main

import "fmt"

type employee struct {
    name   string
    age    int
    salary int
}

func main() {
    emp := employee{name: "Sam", age: 31, salary: 2000}
    fmt.Printf("Emp: %v\n", emp)
    fmt.Printf("Emp: %+v\n", emp)
    fmt.Printf("Emp: %#v\n", emp)
    fmt.Println(emp)
}

Output

Emp: {Sam 31 2000}
Emp: {name:Sam age:31 salary:2000}
Emp: main.employee{name:"Sam", age:31, salary:2000}
{Sam 31 2000}

Printing the struct in JSON form

Second method is to print the struct in the JSON format. Marshal and MarshalIndent function of encoding/json package can be used to print a struct in JSON format. Here is the difference

  • Marshal – Below is the signature of the Marshal function. This function returns the JSON encoding of v by traversing the value recursively
Marshal(v interface{}) ([]byte, error)
  • MarshalIndent– Below is the signature of the MarshalIndent function. It is similar to Marshal function but applies Indent to format the output. So it can be used to pretty print a struct
MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)

It is to be noted that both Marshal and MarshalIndent function can only access the exported fields of a struct, which means that only the capitalized fields can be accessed and encoded in JSON form.

package main

import (
    "encoding/json"
    "fmt"
    "log"
)

type employee struct {
    Name   string
    Age    int
    salary int
}

func main() {
    emp := employee{Name: "Sam", Age: 31, salary: 2000}
    //Marshal
    empJSON, err := json.Marshal(emp)
    if err != nil {
        log.Fatalf(err.Error())
    }
    fmt.Printf("Marshal funnction output %s\n", string(empJSON))

    //MarshalIndent
    empJSON, err = json.MarshalIndent(emp, "", "  ")
    if err != nil {
        log.Fatalf(err.Error())
    }
    fmt.Printf("MarshalIndent funnction output %s\n", string(empJSON))
}

Output:

Marshal funnction output {"Name":"Sam","Age":31}

MarshalIndent funnction output {
  "Name": "Sam",
  "Age": 31
}

The salary field is not printed in the output because it begins with a lowercase letter and is not exported. The Marshal function output is not formatted while the MarshalIndent function output is formatted.

golang also allows the JSON encoded struct key name to be different by the use of struct meta fields as will see in the next section.

Struct Field Meta or Tags

A struct in go also allows adding metadata to its fields. These meta fields can be used to encode decode into different forms, doing some forms of validations on struct fields, etc. So basically any meta information can be stored with fields of a struct and can be used by any package or library for different purposes.

Below is the format for attaching a meta-data. Meta-data is a string literal i.e it is enclosed in backquotes

type strutName struct{
   fieldName type `key:value key2:value2`
}

Now for our use case, we will add JSON tags to employee struct as below. Marshal function will use the key name specified in the tags

type employee struct {
    Name   string `json:"n"`
    Age    int    `json:"a"`
    Salary int    `json:"s"`
}

Let’s see full program

package main

import (
    "encoding/json"
    "fmt"
    "log"
)

type employee struct {
    Name   string `json:"n"`
    Age    int    `json:"a"`
    Salary int    `json:"s"`
}

func main() {
    emp := employee{Name: "Sam", Age: 31, Salary: 2000}
    //Converting to jsonn
    empJSON, err := json.MarshalIndent(emp, '', '  ')
    if err != nil {
        log.Fatalf(err.Error())
    }
    fmt.Println(string(empJSON))
}

Output:

{
  "n": "Sam",
  "a": 31,
  "s": 2000
}

The key name in the output is same as specified in the json meta tags.

Anonymous Fields in a Struct

A struct can have anonymous fields as well, meaning a field having no name. The type will become the field name. In below example, string will be the field name as well

type employee struct {
    string
    age    int
    salary int
}

The anonymous field can also be accessed and assigned a value

package main

import "fmt"

type employee struct {
    string
    age    int
    salary int
}

func main() {
    emp := employee{string: "Sam", age: 31, salary: 2000}
    //Accessing a struct field
    n := emp.string
    fmt.Printf("Current name is: %s\n", n)
    //Assigning a new value
    emp.string = "John"
    fmt.Printf("New name is: %s\n", emp.string)
}

Output

Current name is: Sam
New name is: John

Nested Struct

A struct can have another struct nested in it. Let’s see an example of a nested struct. In below employee struct has address struct nested it in.

package main

import "fmt"

type employee struct {
    name    string
    age     int
    salary  int
    address address
}

type address struct {
    city    string
    country string
}

func main() {
    address := address{city: "London", country: "UK"}
    emp := employee{name: "Sam", age: 31, salary: 2000, address: address}
    fmt.Printf("City: %s\n", emp.address.city)
    fmt.Printf("Country: %s\n", emp.address.country)
}

Output

City: London
Country: UK

Notice how nested struct fields are accessed.

emp.address.city
emp.address.country

Anonymous nested struct fields

The nested struct field can also be anonymous. Also, in this case, nested struct’s fields are directly accessed. So below is valid

emp.city
emp.country

It is also to be noted that below is still valid in this case

emp.address.city
emp.address.country

Let’s see a program

package main

import "fmt"

type employee struct {
	name   string
	age    int
	salary int
	address
}

type address struct {
	city    string
	country string
}

func main() {
	address := address{city: "London", country: "UK"}

	emp := employee{name: "Sam", age: 31, salary: 2000, address: address}

	fmt.Printf("City: %s\n", emp.address.city)
	fmt.Printf("Country: %s\n", emp.address.country)

	fmt.Printf("City: %s\n", emp.city)
	fmt.Printf("Country: %s\n", emp.country)
}

Output

City: London
Country: UK
City: London
Country: UK

Notice in above program that city field of address struct can be accessed in two ways

emp.city
emp.address.city

Similar for the country field of the address struct.

Exported and UnExported fields of a struct

Go doesn’t have any public,  private or protected keyword. The only mechanism to control the visibility outside the package is using the capitalized and non-capitalized formats

  • Capitalized Identifiers are exported. The capital letter indicates that this is an exported identifier and is available outside the package.
  • Non-capitalized identifiers are not exported. The lowercase indicates that the identifier is not exported and will only be accessed from within the same package.

So any struct which starts with a capital letter is exported to other packages.  Similarly any struct field which starts with capital is exported otherwise not. Let’s see an example that shows exporting and non-exporting of structs and struct fields. See model.go and test.go below. Both belong to the main package.

  • Structure
    • Struct Person is exported
    • Struct company is non-exported
  • Structure’s Field
    • Person struct field Name is exported
    • Person struct field age is not exported but Name is exported

model.go

package main

import "fmt"

//Person struct
type Person struct {
    Name string
    age  int
}

type company struct {
}

Let’s write a file test.go in same main package. See below.

test.go

package main

import "fmt"

//Test function
func Test() {
    //STRUCTURE IDENTIFIER
    p := &Person{
        Name: "test",
        age:  21,
    }
    fmt.Println(p)
    c := &company{}
    fmt.Println(c)
    
    //STRUCTURE'S FIELDS
    fmt.Println(p.Name)
    fmt.Println(p.age)
}

On running this file, it is able to access all exported and un-exported fields in model.go as both lies in the same package main. There is no compilation error and it gives below output

Output:

&{test 21}
&{}
test
21

Let’s move the above file model.go to a different package named model. Now notice the output on running ‘go build’. It gives compilation errors. All the compilation error are because test.go in main package to not able to refer to un-exported fields of model.go in model package

model.go

package model

//Person struct
type Person struct {
	Name string
	age  int
}

type company struct {
}

test.go

package main

import (
	"fmt"
        //This will path of your model package
	"/model"
)

//Test function
func main() {
	//STRUCTURE IDENTIFIER
	p := &model.Person{
		Name: "test",
		age:  21,
	}
	fmt.Println(p)
	c := &model.company{}
	fmt.Println(c)

	//STRUCTURE'S FIELDS
	fmt.Println(p.Name)
	fmt.Println(p.age)
}

Output:

cannot refer to unexported name model.company
p.age undefined (cannot refer to unexported field or method age)

Struct Equality

The first thing to know before considering struct equality is weather if all struct fields types are comparable or not

Some of the comparable types as defined by go specification are

  • boolean
  • numeric
  • string,
  • pointer
  • channel
  • interface types
  • structs – if all it’s field type is comparable
  • array – if the type of value of array element is comparable

Some of the types which are not comparable as per go specification and which cannot be used as a key in a map are.

  • Slice
  • Map
  • Function

So two struct will be equal if first all their field types are comparable and all the corresponding field values are equal. Let’s see an example

package main

import "fmt"

type employee struct {
    name   string
    age    int
    salary int
}

func main() {
    emp1 := employee{name: "Sam", age: 31, salary: 2000}
    emp2 := employee{name: "Sam", age: 31, salary: 2000}
    if emp1 == emp2 {
        fmt.Println("emp1 annd emp2 are equal")
    } else {
        fmt.Println("emp1 annd emp2 are not equal")
    }
}

Output

emp1 annd emp2 are equal

If the struct field type are not comparable then there will be compilation error on checking struct equality using the == operator.

package main
import "fmt"
type employee struct {
    name        string
    age         int
    salary      int
    departments []string
}
func main() {
    emp1 := employee{name: "Sam", age: 31, salary: 2000, departments: []string{"CS"}}
    emp2 := employee{name: "Sam", age: 31, salary: 2000, departments: []string{"EC"}}
    if emp1 == emp2 {
        fmt.Println("emp1 annd emp2 are equal")
    } else {
        fmt.Println("emp1 annd emp2 are not equal")
    }
}

Above program will raise compilation error as employee struct contains a field deparments which is a slice of string. slice is not a comparable type and hence the compilation error.

invalid operation: emp1 == emp2 (struct containing []string cannot be compared)

Struct are value types

A struct is value type in go. So a struct variable name is not a pointer to the struct in fact it denotes the entire struct. A new copy of the struct will be created when

  • A struct variable is assigned to another struct variable.
  • A struct variable is passed as an argument to a function.

Let’s see above point with another example

package main

import "fmt"

type employee struct {
    name   string
    age    int
    salary int
}

func main() {
    emp1 := employee{name: "Sam", age: 31, salary: 2000}
    fmt.Printf("Emp1 Before: %v\n", emp1)

    emp2 := emp1

    emp2.name = "John"
    fmt.Printf("Emp1 After assignment: %v\n", emp1)
    fmt.Printf("Emp2: %v\n", emp2)

    test(emp1)
    fmt.Printf("Emp1 After Test Function Call: %v\n", emp1)
}

func test(emp employee) {
    emp.name = "Mike"
    fmt.Printf("Emp in Test function: %v\n", emp)
}

Output

Emp1 Before: {Sam 31 2000}
Emp1 After assignment: {Sam 31 2000}
Emp2: {John 31 2000}
Emp in Test function: {Mike 31 2000}
Emp1 After Test Function Call: {Sam 31 2000}

In above example,

  • we assigned the emp1 to emp2 and we then changed name  emp2 to have a different value.  After that when we print emp1, we see that it hasn’t changed. This is because when we assign emp1 to emp2, a copy is created and changing emp2 doesn’t have any effect on emp1
  • We passed emp1 to the test function and then again changed its name field in the test function.  After that when we print emp1, we see that it hasn’t changed. The reason is same, when emp1 is passed as an argument to test function a copy of emp1 is created.

Conclusion

This is all about struct in golang. In this article, we learned different ways of initializing a struct, pointer to struct, different ways of printing, about anonymous fields, etc. I hope you have liked this article. Please share the feedback/improvements/mistakes in the comments.

Next Tutorial – Array
Previous Tutorial – Pointer

The post Struct in Go (Golang) appeared first on Welcome To Golang By Example.

]]>
https://vikasboss.github.io/struct-in-golang-complete-guide/feed/ 3 2244
Slice in Go (Golang) https://vikasboss.github.io/slice-in-golang/ https://vikasboss.github.io/slice-in-golang/#comments Mon, 18 May 2020 16:52:20 +0000 https://vikasboss.github.io/?p=2181 This is the  chapter 18 of the golang comprehensive tutorial series. Refer to this link for other chapters of the series – Golang Comprehensive Tutorial Series Next Tutorial – MapsPrevious Tutorial – Array Now let’s check...

The post Slice in Go (Golang) appeared first on Welcome To Golang By Example.

]]>
This is the  chapter 18 of the golang comprehensive tutorial series. Refer to this link for other chapters of the series – Golang Comprehensive Tutorial Series

Next Tutorial – Maps
Previous Tutorial – Array

Now let’s check out the current tutorial. Below is the table of contents for current tutorial.

Overview

The size of the array being part of it limits the expressiveness and power of array in go. This is where slice comes into the picture. Slice is more powerful and convenient to use than an array.  Slice, in fact, is more analogous to arrays in another programming language.

A slice points to an underlying array and is internally represented by a slice header.  Unlike array, the size of a slice is flexible and can be changed.

Internal representation of a slice

Internally a slice is represented by three things.

  • Pointer to the underlying array
  • Current length of the underlying array
  • Total Capacity which is the maximum capacity to which the underlying array can expand.

Above internal representation is described by SliceHeader struct which looks like this:

type SliceHeader struct {
        Pointer uintptr
        Len  int
        Cap  int
}

The Pointer field in the slice header is a pointer to the underlying array.  Len is the current length of the slice and Cap is the capacity of the slice. Similar to array a slice index starts from zero till length_of_slice-1. So a slice of 3 lengths and 5 capacity will look like below

Creating a slice

There are four ways of creating a slice

  • Using the []<type>{} format
  • Creating a slice from another slice or array
  • Using make
  • Using new

Let’s look at each of above method one by one.

Using the []<type>{} format

The most common way of declaring a slice is this

s := []int

It declares an empty of slice of 0 length and 0 capacity. We can also initialise the slice during declaration.

s := []int{1,2}

It declares a slice of integers of length 2 and also the capacity of 2. The capacity will be equal to the actual slice elements specified. We also have two library functions provided by go which can be used to know the length and capacity of a slice.

  • len() function – for  length of the slice
  • cap() function – for capacity of the slice

Let’s see a small program which shows the above points

package main

import "fmt"

func main() {
    sample := []int{}
    fmt.Println(len(sample))
    fmt.Println(cap(sample))
    fmt.Println(sample)

    letters := []string{"a", "b", "c"}
    fmt.Println(len(letters))
    fmt.Println(cap(letters))
    fmt.Println(letters)
}

Output

0
0
[]

3
3
[a b c]

When the actual elements are not specified, then both length and capacity of the slice is zero. When actual elements are specified , both length and capacity is equal to the number of actual elements specified.

Creating a slice from another slice or array

A slice can be created by re-slicing an exiting slice or array.

Create a slice from Array

The format for creating a new slice by re-slicing an existing array is

[n]sample[start:end]

The above operation will return a new slice from the array starting from index start to index end-1. So the element at index end is not included in the newly created slice. While re-slicing , both start and end index is optional.

  • The default value of the start index is zero
  • The default value of the end index is the length of the array

Let’s see an example.

package main

import "fmt"

func main() {
    numbers := [5]int{1, 2, 3, 4, 5}

    //Both start and end
    num1 := numbers[2:4]
    fmt.Println("Both start and end")
    fmt.Printf("num1=%v\n", num1)
    fmt.Printf("length=%d\n", len(num1))
    fmt.Printf("capacity=%d\n", cap(num1))

    //Only start
    num2 := numbers[2:]
    fmt.Println("\nOnly start")
    fmt.Printf("num1=%v\n", num2)
    fmt.Printf("length=%d\n", len(num2))
    fmt.Printf("capacity=%d\n", cap(num2))

    //Only end
    num3 := numbers[:3]
    fmt.Println("\nOnly end")
    fmt.Printf("num1=%v\n", num3)
    fmt.Printf("length=%d\n", len(num3))
    fmt.Printf("capacity=%d\n", cap(num3))

    //None
    num4 := numbers[:]
    fmt.Println("\nOnly end")
    fmt.Printf("num1=%v\n", num4)
    fmt.Printf("length=%d\n", len(num4))
    fmt.Printf("capacity=%d\n", cap(num4))
}

Output

Both start and end
num1=[3 4]
length=2
capacity=3

Only start
num1=[3 4 5]
length=3
capacity=3

Only end
num1=[1 2 3]
length=3
capacity=5

Only end
num1=[1 2 3 4 5]
length=5
capacity=5

Notice in above example that

  • length of newly created slice = (endstart)
  • capacity of newly created slice = (length_of_arraystart)

The num1 slice would look like

The newly created slices still refer the original array. To check this change element at any one of the index of the array and then reprint the slice

numbers[3] = 8
fmt.Printf("num1=%v\n", num2)
fmt.Printf("num3=%v\n", num3)
fmt.Printf("num4=%v\n", num4)

Here is the output:

num1=[3 8 5]
num3=[1 2 3 8]
num4=[1 2 3 8 5]

This proves that each of the new slice is still referring to the original array.

Create a slice from slice

Whatever we discussed about re-slicing from an array also applies here as well. See below example which illustrates same thing

package main

import "fmt"

func main() {
    numbers := []int{1, 2, 3, 4, 5}

    //Both start and end
    num1 := numbers[2:4]
    fmt.Println("Both start and end")
    fmt.Printf("num1=%v\n", num1)
    fmt.Printf("length=%d\n", len(num1))
    fmt.Printf("capacity=%d\n", cap(num1))

    //Only start
    num2 := numbers[2:]
    fmt.Println("\nOnly start")
    fmt.Printf("num1=%v\n", num2)
    fmt.Printf("length=%d\n", len(num2))
    fmt.Printf("capacity=%d\n", cap(num2))

    //Only end
    num3 := numbers[:3]
    fmt.Println("\nOnly end")
    fmt.Printf("num1=%v\n", num3)
    fmt.Printf("length=%d\n", len(num3))
    fmt.Printf("capacity=%d\n", cap(num3))

    //None
    num4 := numbers[:]
    fmt.Println("\nOnly end")
    fmt.Printf("num1=%v\n", num4)
    fmt.Printf("length=%d\n", len(num4))
    fmt.Printf("capacity=%d\n", cap(num4))
}

Output

Both start and end
num1=[3 4]
length=2
capacity=3

Only start
num1=[3 4 5]
length=3
capacity=3

Only end
num1=[1 2 3]
length=3
capacity=5

Only end
num1=[1 2 3 4 5]
length=5
capacity=5

Here also the newly created slices refer to the same underlying array that was being referred to by the original slice.  To check this change element at any one of the index of the original slice and then reprint all the newly created slices

numbers[3] = 8
fmt.Printf("num1=%v\n", num2)
fmt.Printf("num3=%v\n", num3)
fmt.Printf("num4=%v\n", num4)

Here is the output:

num1=[3 8 5]
num3=[1 2 3 8]
num4=[1 2 3 8 5]

Using the make function

make is a builtin function provided by go that can also be used to create a slice. Below is the signature of make function

func make([]{type}, length, capacity int) []{type}

Capacity is an optional parameter while creating slice using the make function. When capacity is omitted, the capacity of the slice is equal length specified for the slice. When using make function, behind the scenes go allocates an array equal to the capacity. All the elements of the allocated array are initialized with default zero value of the type. Let’s see a program illustrating this point.

package main

import "fmt"

func main() {
    numbers := make([]int, 3, 5)
    fmt.Printf("numbers=%v\n", numbers)
    fmt.Printf("length=%d\n", len(numbers))
    fmt.Printf("capacity=%d\n", cap(numbers))

    //With capacity ommited
    numbers = make([]int, 3)
    fmt.Println("\nCapacity Ommited")
    fmt.Printf("numbers=%v\n", numbers)
    fmt.Printf("length=%d\n", len(numbers))
    fmt.Printf("capacity=%d\n", cap(numbers))
}

Output

numbers=[0 0 0]
length=3
capacity=5

Capacity Ommited
numbers=[0 0 0]
length=3
capacity=3

Using the new function

new is a builtin function provided by go that can also be used to create a slice. It is not a very popular way of creating a slice as make is much more flexible in terms of functionalities . It is not generally used and also using new function returns a pointer to nil slice. Let’s see an example. In below example we are using the dereferencing operator ‘*’ as new function returns a pointer to the nil slice.

package main

import "fmt"

func main() {
    numbers := new([]int)
    fmt.Printf("numbers=%v\n", *numbers)
    fmt.Printf("length=%d\n", len(*numbers))
    fmt.Printf("capacity=%d\n", cap(*numbers))
}

Output

numbers=[]
length=0
capacity=0

Length vs Capacity

Before moving further, let’s emphasis on understanding the caveats of length and capacity. Let’s create a simple slice with capacity greater than length.

numbers := make([]int, 3, 5)
  • Accessing the slice behind its length will result in a run time error “Index out of range”. It doesn’t matter if the accessed index is within the capacity. So the below line will cause the run time error.
numbers[4] = 5
  • The length of the slice can be increased up to its capacity by re-slicing. So below re-slice will increase the length from 3 to 5.
numbers = numbers[0:5]
  • The length of the slice can also be decreased using re-slicing. So below re-slice will decrease the length from 3 to 2
numbers = numbers[0:2]
  • The advantage of having capacity is that array of size capacity can be pre-allocated during the initialization.  This is a performance boost as if more elements are needed to include in this array then space is already allocated for them.

Let’s see the program illustrating above points

package main

import "fmt"

func main() {
    numbers := make([]int, 3, 5)
    fmt.Printf("numbers=%v\n", numbers)
    fmt.Printf("length=%d\n", len(numbers))
    fmt.Printf("capacity=%d\n", cap(numbers))

    //This line will cause a runtime error index out of range [4] with length 3
    //numbers[4] = 5
   
    //Increasing the length from 3 to 5
    numbers = numbers[0:5]
    fmt.Println("\nIncreasing length from 3 to 5")
    fmt.Printf("numbers=%v\n", numbers)
    fmt.Printf("length=%d\n", len(numbers))
    fmt.Printf("capacity=%d\n", cap(numbers))

    //Decresing the length from 3 to 2
    numbers = numbers[0:2]
    fmt.Println("\nDecreasing length from 3 to 2")
    fmt.Printf("numbers=%v\n", numbers)
    fmt.Printf("length=%d\n", len(numbers))
    fmt.Printf("capacity=%d\n", cap(numbers))
}

Output

numbers=[0 0 0]
length=3
capacity=5

Increasing length from 3 to 5
numbers=[0 0 0 0 0]
length=5
capacity=5

Decreasing length from 3 to 2
numbers=[0 0]
length=2
capacity=5

Accessing and Modifying Slice Elements

A slice element can be accessed by specifying the index. Slice element can also be allotted a new value using the index. Also, note that any changes in the underlying array will reflect back in the slice as we have also seen above. Let’s see a small example of accessing and modifying

package main

import "fmt"

func main() {
    array := [5]int{1, 2, 3, 4, 5}
    slice := array[:]

    //Modifying the slice
    slice[1] = 7
    fmt.Println("Modifying Slice")
    fmt.Printf("Array=%v\n", array)
    fmt.Printf("Slice=%v\n", slice)

    //Modifying the array. Would reflect back in slice too
    array[1] = 2
    fmt.Println("\nModifying Underlying Array")
    fmt.Printf("Array=%v\n", array)
    fmt.Printf("Slice=%v\n", slice)
}

Output

Modifying Slice
Array=[1 7 3 4 5]
Slice=[1 7 3 4 5]

Modifying Underlying Array
Array=[1 2 3 4 5]
Slice=[1 2 3 4 5]

Different ways of iterating a slice

An array can be iterated using:

  • Using for loop
  • Using for-range loop

Let’s see a code example for both

package main

import "fmt"

func main() {
    letters := []string{"a", "b", "c"}
    //Using for loop
    fmt.Println("Using for loop")
    len := len(letters)
    for i := 0; i < len; i++ {
        fmt.Println(letters[i])
    }

    //Using for-range operator
    fmt.Println("\nUsing for-range loop")
    for i, letter := range letters {
        fmt.Printf("%d %s\n", i, letter)
    }
}

Output

Using for loop
a
b
c

Using for-range loop
0 a
1 b
2 c

Appending to a slice

go builtin package provides an append function that can be used to append to a slice at the end. Below is the signature of this function

func append(slice []Type, elems ...Type) []Type

The first argument is the slice itself. The second is the variable number of arguments which is

elems ...Type

'...' operator is the variadic syntax. So basically ...Type means It means that the append function can accept variable number of arguments of type Type. Below is the way for using this function. In below code we are appending 4 to a slice which has two elements. It appends at the end and returns the original slice. That is why we are collecting result again in numbers variable. It is also ok to assign the result to some other variable.

numbers := []int{1,2}
numbers = append(numbers, 4) //Slice will become [1, 2, 4]

It is also ok to append many number of elements because the second argument is the variadic argument.

numbers := []int{1,2}
numbers = append(numbers, 3, 4, 5) //Slice will become [1, 2, 3, 4, 5]

This function in the background increases the length and capacity of the slice. There are two cases

  • When slice length is less than capacity.

In this case, on using the append function,  the length of the slice will be increased  by one without any change in its capacity. Let's see a example

package main

import "fmt"

func main() {
    numbers := make([]int, 3, 5)
    numbers[0] = 1
    numbers[1] = 2
    numbers[2] = 3
    fmt.Printf("numbers=%v\n", numbers)
    fmt.Printf("length=%d\n", len(numbers))
    fmt.Printf("capacity=%d\n", cap(numbers))

    //Append number 4
    numbers = append(numbers, 4)
    fmt.Println("\nAppend Number 4")
    fmt.Printf("numbers=%v\n", numbers)
    fmt.Printf("length=%d\n", len(numbers))
    fmt.Printf("capacity=%d\n", cap(numbers))

    //Append number 5
    numbers = append(numbers, 4)
    fmt.Println("\nAppend Number 5")
    fmt.Printf("numbers=%v\n", numbers)
    fmt.Printf("length=%d\n", len(numbers))
    fmt.Printf("capacity=%d\n", cap(numbers))
}

Output

numbers=[1 2 3]
length=3
capacity=5

Append Number 4
numbers=[1 2 3 4]
length=4
capacity=5

Append Number 5
numbers=[1 2 3 4 4]
length=5
capacity=5

Capacity in all cases doesn't changes and it is 5 while length increases by 1.

  • When slice length is greater than capacity.

In this case since there is no more capacity, so no new elements can be accommodated.  So in this case under the hood an array of double the capacity will be allocated. The current array pointed by the  slice will be copied to that new array. Now the slice will starting pointing to this new array. Hence the capacity will be doubled and length will be increased by 1. Let's see a example

package main

import "fmt"

func main() {
    numbers := make([]int, 3, 3)
    numbers[0] = 1
    numbers[1] = 2
    numbers[2] = 3

    fmt.Printf("numbers=%v\n", numbers)
    fmt.Printf("length=%d\n", len(numbers))
    fmt.Printf("capacity=%d\n", cap(numbers))

    //Append number 4
    numbers = append(numbers, 4)
    fmt.Println("\nAppend Number 4")
    fmt.Printf("numbers=%v\n", numbers)
    fmt.Printf("length=%d\n", len(numbers))
    fmt.Printf("capacity=%d\n", cap(numbers))
}

Output

numbers=[1 2 3]
length=3
capacity=3

Append Number 4
numbers=[1 2 3 4]
length=4
capacity=6

Notice in above example that the capacity is doubled.

It is also possible to append one slice to another slice. Below is the format for that.

res := append(slice1, slice2...)

Notice '...' after the second slice. '...' is the operator which means that the argument is a variadic parameter. Meaning that during run time slice2 will be expanded to its individual elements which are passed as multiple arguments to the append function.

package main

import "fmt"

func main() {
    numbers1 := []int{1, 2}
    numbers2 := []int{3, 4}
    numbers := append(numbers1, numbers2...)
    fmt.Printf("numbers=%v\n", numbers)
    fmt.Printf("length=%d\n", len(numbers))
    fmt.Printf("capacity=%d\n", cap(numbers))
}

Output

numbers=[1 2 3 4]
length=4
capacity=4

Copy a slice

go builtin package provides copy function that can be used to copy a slice. Below is the signature of this function. It takes in two slices dst and src, and copies data from src to dst. It returns the number of elements copied.

func copy(dst, src []Type) int

There are two cases to be considered while using the copy function:

  • If the length of src is greater than the length of dst, then the number of elements copied is the length of dst
  • If the length of dst is greater than the length of src, then the number of elements copied is the length of src

Basically the number of elements copied is minimum of length of (src, dst). 

Also to note then once the copy is done then any change in dst will not reflect in src and vice versaLet's see an example of it.

package main

import "fmt"

func main() {
    src := []int{1, 2, 3, 4, 5}
    dst := make([]int, 5)

    numberOfElementsCopied := copy(dst, src)
    fmt.Printf("Number Of Elements Copied: %d\n", numberOfElementsCopied)
    fmt.Printf("dst: %v\n", dst)
    fmt.Printf("src: %v\n", src)

    //After changing numbers2
    dst[0] = 10
    fmt.Println("\nAfter changing dst")
    fmt.Printf("dst: %v\n", dst)
    fmt.Printf("src: %v\n", src)
}

Output

Number Of Elements Copied: 5
dst: [1 2 3 4 5]
src: [1 2 3 4 5]

After changing dst
dst: [10 2 3 4 5]
src: [1 2 3 4 5]

Nil Slice

The default zero value of a slice is nil. The length and capacity both of a nil slice is zero. Though it is possible to append to a nil slice as well. Let's see an example

package main

import "fmt"

func main() {
    var numbers []int
    fmt.Printf("numbers=%v\n", numbers)
    fmt.Printf("length=%d\n", len(numbers))
    fmt.Printf("capacity=%d\n", cap(numbers))
    numbers = append(numbers, 1)
    fmt.Printf("numbers=%v\n", numbers)
    fmt.Printf("length=%d\n", len(numbers))
    fmt.Printf("capacity=%d\n", cap(numbers))
}

Output

numbers=[]
length=0
capacity=0
numbers=[1]
length=1
capacity=1

Multidimensional Slices

As the multi-dimensional array is an array of arrays, similarly multi-dimensional slice is a slice of slices. To understand this, let's first look at the definition of a slice.

Data field in the slice header is a pointer to the underlying array. For a one dimensional slice, we have below declaration

oneDSlice := make([]int, 2)

To declare a two dimensional slice the declaration would be

twoDSlice = make([][]int, 2)

Above declaration means that we want to create a slice of 2 slices. Carefully understand this point. But wait a second here, we haven't specified the second dimension here, meaning what is the length of each of the inner 2 slices. In case of slice, each of the inner slice has to be explicitly intialized like below

for i := range twoDSlice {
    twoDSlice[i] = make([]int, 3)
}

So using range on the original slice, we specify the length each of 2 slices using make.  Below is one other way of doing the same but with slice elements specified

var twoDSlice = make([][]int, 2)
twoDSlice[0] = []int{1, 2, 3}
twoDSlice[1] = []int{4, 5, 6}

Basically, with the above declaration, we create a slice of 2*3 dimensions which is a two-dimensional slice. The same idea can be extended to two-dimension, three-dimension, and so on.

A complete working example of above two points

package main

import "fmt"

func main() {
    twoDSlice1 := make([][]int, 3)
    for i := range twoDSlice1 {
        twoDSlice1[i] = make([]int, 3)
    }
    fmt.Printf("Number of rows in slice: %d\n", len(twoDSlice1))
    fmt.Printf("Number of columns in arsliceray: %d\n", len(twoDSlice1[0]))
    fmt.Printf("Total number of elements in slice: %d\n", len(twoDSlice1)*len(twoDSlice1[0]))
    fmt.Println("First Slice")
    for _, row := range twoDSlice1 {
        for _, val := range row {
            fmt.Println(val)
        }
    }
    twoDSlice2 := make([][]int, 2)
    twoDSlice2[0] = []int{1, 2, 3}
    twoDSlice2[1] = []int{4, 5, 6}
    fmt.Println()
    fmt.Printf("Number of rows in slice: %d\n", len(twoDSlice2))
    fmt.Printf("Number of columns in arsliceray: %d\n", len(twoDSlice2[0]))
    fmt.Printf("Total number of elements in slice: %d\n", len(twoDSlice2)*len(twoDSlice2[0]))
    fmt.Println("Second Slice")
    for _, row := range twoDSlice2 {
        for _, val := range row {
            fmt.Println(val)
        }
    }
}

Output

Number of rows in slice: 2
Number of columns in arsliceray: 3
Total number of elements in slice: 6
First Slice
0
0
0
0
0
0

Number of rows in slice: 2
Number of columns in arsliceray: 3
Total number of elements in slice: 6
Second Slice
1
2
3
4
5
6

We mentioned above that we are creating a two-dimensional slice of 2*3 dimensions.  With that said the thought that might be coming to your mind is whether it is possible to have different lengths for inner slices. Yes, it is possible. Unlike arrays which have inner arrays of the same length, in case of slice since we initialize each of the inner slices individually, it is possible to have different length for inner slices

Let's see an example

package main

import "fmt"

func main() {
    twoDSlice := make([][]int, 2)
    twoDSlice[0] = []int{1, 2, 3}
    twoDSlice[1] = []int{4, 5}
  
    fmt.Printf("Number of rows in slice: %d\n", len(twoDSlice))
    fmt.Printf("Len of first row: %d\n", len(twoDSlice[0]))
    fmt.Printf("Len of second row: %d\n", len(twoDSlice[1]))
    fmt.Println("Traversing slice")
    for _, row := range twoDSlice {
        for _, val := range row {
            fmt.Println(val)
        }
    }
}

Output

Number of rows in slice: 2
Len of first row: 3
Len of second row: 2
Traversing slice
1
2
3
4
5

Let's see a small example of a three-dimensional slice as well. In the below program, we are creating a slice of 2*2*3 dimensions.

package main

import "fmt"

func main() {
    sample := make([][][]int, 2)
    for i := range sample {
        sample[i] = make([][]int, 2)
        for j := range sample[i] {
            sample[i][j] = make([]int, 3)
        }
    }
    
    fmt.Printf("Length of first dimension: %d\n", len(sample))
    fmt.Printf("Length of second dimension: %d\n", len(sample[0]))
    fmt.Printf("Length of third dimension: %d\n", len(sample[0][0]))
    fmt.Printf("Overall Dimension of the slice: %d*%d*%d\n", len(sample), len(sample[0]), len(sample[0][0]))
    fmt.Printf("Total number of elements in slice: %d\n", len(sample)*len(sample[0])*len(sample[0][0]))
    for _, first := range sample {
        for _, second := range first {
            for _, value := range second {
                fmt.Println(value)
            }
        }
    }
}

Output

Length of first dimension: 2
Length of second dimension: 2
Length of third dimension: 3
Overall Dimension of the slice: 2*2*3
Total number of elements in slice: 12
0
0
0
0
0
0
0
0
0
0
0
0

Conclusion

This is all about slice in golang. Hope you have liked this article. Please share feedback/improvements/mistakes in comments

Next Tutorial – Maps
Previous Tutorial – Array

The post Slice in Go (Golang) appeared first on Welcome To Golang By Example.

]]>
https://vikasboss.github.io/slice-in-golang/feed/ 8 2181
Understanding Array in Go (Golang) – Complete Guide https://vikasboss.github.io/understanding-array-golang-complete-guide/ https://vikasboss.github.io/understanding-array-golang-complete-guide/#comments Wed, 13 May 2020 19:52:44 +0000 https://vikasboss.github.io/?p=2137 This is the  chapter 17 of the golang comprehensive tutorial series. Refer to this link for other chapters of the series – Golang Comprehensive Tutorial Series Next Tutorial – SlicePrevious Tutorial – Struct Now let’s check...

The post Understanding Array in Go (Golang) – Complete Guide appeared first on Welcome To Golang By Example.

]]>
This is the  chapter 17 of the golang comprehensive tutorial series. Refer to this link for other chapters of the series – Golang Comprehensive Tutorial Series

Next Tutorial – Slice
Previous Tutorial – Struct

Now let’s check out the current tutorial. Below is the table of contents for current tutorial.

Overview

Similar to any other programming language, golang also has array data structure. But in go, arrays behave little differently than other languages and also we have something called slice in golang which is like a reference to an array. In this article, we will study only array.

Definition

An array is a contiguous collection of elements of the same type. It is an ordered sequence of elements stored contiguously in memory

Here is the format for the declaration of an array

sample := [size_of_array]{type}{a1, a2... an}
  • size_of_array – number of elements in the array
  • <type> is type of each element in the array
  • a1, a2 … an are the actual elements.

In golang, the size of the array is part of its type. So  This means that two arrays that have a different number of elements are of two different types and one cannot be assigned to another. Below error will be raised in case we try to assign two arrays of different length

cannot use sample1 (type [1]int) as type [2]int in assignment

The code is:

sample1 := [1]int{1}
sample2 := [2]int{1,2}

sample2 = sample1

For the same reason the length of array is fixed during create and cannot be changed later.

Declaration of an array

Both number of elements and actual elements are optional in the array declaration.

In below example, we see 4 ways of declaring of an array

  • Specifying both the length of the array and actual elements. Eg.
[2]int{1, 2}
  • Only length – In this case all the actual elements are filled up with default value zero of that type. Eg
[2]int{}
  • Only actual elements – In this case, the length of array will be equal to the number of actual elements. The symbol ‘…’ needs to be used within square brackets like this […] when not specifying the length. The symbol is an instruction to the compiler to calculate the length.
[...]int{2, 3}
  • Without length and actual elements – an empty array will be created in this case. Similar to above the symbol ‘…’ also needs to be used in this case as well.
[...]int{}

Let’s see a code example illustrating above points. Also please keep in mind that the builtin function len() can be used to calculate the length of an array. In below program we are using len() function to calculate the length of the array.

package main

import "fmt"

func main() {
    //Both number of elements and actual elements
    sample1 := [2]int{1, 2}
    fmt.Printf("Sample1: Len: %d, %v\n", len(sample1), sample1)

    //Only actual elements
    sample2 := [...]int{2, 3}
    fmt.Printf("Sample2: Len: %d, %v\n", len(sample2), sample2)

    //Only number of elements
    sample3 := [2]int{}
    fmt.Printf("Sample3: Len: %d, %v\n", len(sample3), sample3)

    //Without both number of elements and actual elements
    sample4 := [...]int{}
    fmt.Printf("Sample4: Len: %d, %v\n", len(sample4), sample4)
}

Output

Sample1: Len: 2, [1 2]
Sample2: Len: 2, [2 3]
Sample3: Len: 2, [0 0]
Sample4: Len: 0, []

Notice in the above example that for sample3 variable the actual elements are filled up with the default value of int which is 0.

It is also ok if the actual elements specified are less than the length of the array. The rest of the elements are filled up with the default value of the type specified. See the below example. The length of the array specified is 4 while only 2 actual elements are declared. Hence the remaining two elements are assigned value 0 which is the default zero value of an int.

package main

import "fmt"

func main() {
    sample := [4]int{5, 8}
    fmt.Printf("Sample: Len: %d, %v\n", len(sample), sample)
}

Output

Sample: Len: 4, [5 8 0 0]

Accessing array elements

Since array element are stored in contiguous manner, we can access an array element using an index. Similarly individual array elements can also be assigned a value using index. Accessing out of bound index will cause a compilation error. See below examples illustrating these points. The first index position will be zero and last will (length_of_array-1)

package main

import "fmt"

func main() {
    sample := [2]string{"aa", "bb"}

    fmt.Println(sample[0])
    fmt.Println(sample[1])

    sample[0] = "xx"
    fmt.Println(sample)
    //sample[3] = "yy"
}

Output

aa
bb
[xx bb]

On uncommenting the below line

sample[3] = "yy"

, it will give compilation error

invalid array index 3 (out of bounds for 2-element array)

Arrays are value in go

Array are value type in go. So an array variable name is not a pointer to the first element in fact it denotes the entire array. A copy of the array will be created when

  • An array variable is assigned to another array variable.
  • An array variable is passed as an argument to a function.

Let’s see above point with another example

package main

import "fmt"

func main() {
    sample1 := [2]string{"a", "b"}
    fmt.Printf("Sample1 Before: %v\n", sample1)
    sample2 := sample1
    sample2[0] = "c"
    fmt.Printf("Sample1 After assignment: %v\n", sample1)
    fmt.Printf("Sample2: %v\n", sample2)
    test(sample1)
    fmt.Printf("Sample1 After Test Function Call: %v\n", sample1)
}
func test(sample [2]string) {
    sample[0] = "d"
    fmt.Printf("Sample in Test function: %v\n", sample)
}

Output

Sample1 Before: [a b]
Sample1 After assignment: [a b]
Sample2: 
Sample in Test function: [d b]
Sample1 After Test Function Call: [a b]

In above example,

  • we assigned the sample1 to sample2 and we then changed 0th index at sample2 to have a different value.  After that when we print sample1, we see that it hasn’t changed. This is because when we assign sample1 to sample2, a copy is created and changing sample2 doesn’t have any effect on sample1
  • We passed sample1 to the test function and then again changed its value in the test function at 0th index.  After that when we print sample1, we see that it hasn’t changed. The reason is same, when sample1 is passed as an argument to test function a copy of sample1 is created.

Different ways of iterating an array

An array can be iterated using:

  • Using for loop
  • Using for-range loop

Let’s see a code example for both

package main

import "fmt"

func main() {
    letters := [3]string{"a", "b", "c"}
    //Using for loop
    fmt.Println("Using for loop")
    len := len(letters)
    for i := 0; i < len; i++ {
        fmt.Println(letters[i])
    }
    //Using for-range operator
    fmt.Println("\nUsing for-range loop")
    for i, letter := range letters {
        fmt.Printf("%d %s\n", i, letter)
    }
}

Output

Using for loop
a
b
c

Using for-range loop
0 a
1 b
2 c

MultiDimensional Arrays

Below is the format for declaring a two dimensional array

sample := [x][y]{type}{{a11, a12 .. a1y},
                       {a21, a22 .. a2y},
                       {.. },
                       {ax1, ax2 .. axy}}     

where

  • x denotes the number of rows
  • y denotes the number of columns
  • aij denotes an element present at i row and j column

The same idea can be extended to three dimensions, four dimensions, and so on. All the rules we discussed above also apply to multidimensional arrays too.

Let's see a code example

package main

import "fmt"

func main() {
    sample := [2][3]int{{1, 2, 3}, {4, 5, 6}}
    fmt.Println("First Run")
    for _, row := range sample {
        for _, val := range row {
            fmt.Println(val)
        }
    }

    sample[0][0] = 6
    sample[1][2] = 1
    fmt.Println("\nSecond Run")
    for _, row := range sample {
        for _, val := range row {
            fmt.Println(val)
        }
    }
}

Output

First Run
1
2
3
4
5
6

Second Run
6
2
3
4
5
1

In above example we access the element of two dimensional array using index for both first and second dimension

sample[0][0] = 6

Also notice how we are traversing the two dimensional array. We need to use nested range . The first range traverses the arrays of array. The second range traverses the individual array after that.

Conclusion

This is all about array in Golang. Hope you have liked this article. Please share feedback/improvements/mistakes in comments.

Next Tutorial – Slice
Previous Tutorial – Struct

The post Understanding Array in Go (Golang) – Complete Guide appeared first on Welcome To Golang By Example.

]]>
https://vikasboss.github.io/understanding-array-golang-complete-guide/feed/ 8 2137
Understanding time and date in Go (Golang) – Complete Guide https://vikasboss.github.io/all-about-time-and-date-golang/ https://vikasboss.github.io/all-about-time-and-date-golang/#comments Sat, 01 Feb 2020 08:08:05 +0000 https://vikasboss.github.io/?p=1292 Note: If you are interested in learning Golang, then for that we have a golang comprehensive tutorial series. Do check it out – Golang Comprehensive Tutorial Series. Now let’s see the current tutorial....

The post Understanding time and date in Go (Golang) – Complete Guide appeared first on Welcome To Golang By Example.

]]>
Note: If you are interested in learning Golang, then for that we have a golang comprehensive tutorial series. Do check it out – Golang Comprehensive Tutorial Series. Now let’s see the current tutorial. Below is the table of contents.

Overview

Time or Date is represented in Go using time.Time struct. time can be also be represented as a

  • Unix Time (Also known as Epoch Time) – It is the number of seconds elapsed since 00:00:00 UTC on 1 January 1970. This time is also known as the Unix epoch.

Structure

time.Time object is used to represent a specific point in time. The time.Time struct is as below

type Time struct {
    // wall and ext encode the wall time seconds, wall time nanoseconds,
    // and optional monotonic clock reading in nanoseconds.
    wall uint64
    ext  int64
    //Location to represent timeZone
    // The nil location means UTC
    loc *Location
}

As you can notice that every time.Time object has an associated location value which is used to determine the minute, hour, month, day and year corresponding to that time.

Create a new time

Using time.Now()

This function can be used to get the current local timestamp. The signature of the function is

func Now() Time

Using time.Date()

This function returns the time which is yyyy-mm-dd hh:mm:ss + nsec nanoseconds with the appropriate time zone corresponding to the given location. The signature of the function is:

func Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) Time

Understanding Duration

duration is the time that has elapsed between two instants of time. It is represented as int64nanosecond count. So duration is nothing in Go but just a number representing time in nanoseconds. So if duration value is  equal to 1000000000 then it represents 1 sec or 1000 milliseconds or 10000000000 nanoseconds

As an example duration between two time values 1 hour apart will be below value which is equal number of nanoseconds in 1 hour.

1 *60*60*1000*1000*1000

It is represented as below in the time package.

type Duration int64

Below are some common duration which are defined in time package

const (
    Nanosecond  Duration = 1
    Microsecond          = 1000 * Nanosecond
    Millisecond          = 1000 * Microsecond
    Second               = 1000 * Millisecond
    Minute               = 60 * Second
    Hour                 = 60 * Minute
)

Some of the function defined on time.Time object that returns the Duration are

  • func (t Time) Sub(u Time) Duration – It returns the duration t-u
  • func Since(t Time) Duration – It returns the duration which has elapsed since t
  • func Until(t Time) Duration – It returns the duration until t

Add or Subtract to a time

Now that you have understood what duration , let’s see how we can add or subtract to a time instance

time package in golang defines two ways of adding or subtracting to a time.

  • Add function – It is used to add/subtract a duration to time t. Since duration can be represented in hours, minutes, seconds, milliseconds, microseconds and nanoseconds, therefore Add function can be used to add/subtract hours, minutes, seconds, milliseconds, microseconds and nanoseconds from a time . Its signature is
func (t Time) Add(d Duration) Time
  • AddDate function – It is used to add/subtract years, months and days to time t. Its signature is
func (t Time) AddDate(years int, months int, days int) Time

Note: Positive values are used to add to time and negative values are used to subtract. Let’s see a working example of Add and Subtract to time.

Add to time

Below code can be used to add to time

package main

import (
    "fmt"
    "time"
)

func main() {
    t := time.Now()

    //Add 1 hours
    newT := t.Add(time.Hour * 1)
    fmt.Printf("Adding 1 hour\n: %s\n", newT)

    //Add 15 min
    newT = t.Add(time.Minute * 15)
    fmt.Printf("Adding 15 minute\n: %s\n", newT)

    //Add 10 sec
    newT = t.Add(time.Second * 10)
    fmt.Printf("Adding 10 sec\n: %s\n", newT)

    //Add 100 millisecond
    newT = t.Add(time.Millisecond * 10)
    fmt.Printf("Adding 100 millisecond\n: %s\n", newT)

    //Add 1000 microsecond
    newT = t.Add(time.Millisecond * 10)
    fmt.Printf("Adding 1000 microsecond\n: %s\n", newT)

    //Add 10000 nanosecond
    newT = t.Add(time.Nanosecond * 10000)
    fmt.Printf("Adding 1000 nanosecond\n: %s\n", newT)

    //Add 1 year 2 month 4 day
    newT = t.AddDate(1, 2, 4)
    fmt.Printf("Adding 1 year 2 month 4 day\n: %s\n", newT)
}

Output:

Adding 1 hour:
 2020-02-01 02:16:35.893847 +0530 IST m=+3600.000239893

Adding 15 minute:
 2020-02-01 01:31:35.893847 +0530 IST m=+900.000239893

Adding 10 sec:
 2020-02-01 01:16:45.893847 +0530 IST m=+10.000239893

Adding 100 millisecond:
 2020-02-01 01:16:35.903847 +0530 IST m=+0.010239893

Adding 1000 microsecond:
 2020-02-01 01:16:35.903847 +0530 IST m=+0.010239893

Adding 1000 nanosecond:
 2020-02-01 01:16:35.893857 +0530 IST m=+0.000249893

Adding 1 year 2 month 4 day:
 2021-04-05 01:16:35.893847 +0530 IST

Subtract to time

Below code can be used to subtract to time

package main

import (
    "fmt"
    "time"
)

func main() {
    t := time.Now()

    //Add 1 hours
    newT := t.Add(-time.Hour * 1)
    fmt.Printf("Subtracting 1 hour:\n %s\n", newT)

    //Add 15 min
    newT = t.Add(-time.Minute * 15)
    fmt.Printf("Subtracting 15 minute:\n %s\n", newT)

    //Add 10 sec
    newT = t.Add(-time.Second * 10)
    fmt.Printf("Subtracting 10 sec:\n %s\n", newT)

    //Add 100 millisecond
    newT = t.Add(-time.Millisecond * 10)
    fmt.Printf("Subtracting 100 millisecond:\n %s\n", newT)

    //Add 1000 microsecond
    newT = t.Add(-time.Millisecond * 10)
    fmt.Printf("Subtracting 1000 microsecond:\n %s\n", newT)

    //Add 10000 nanosecond
    newT = t.Add(-time.Nanosecond * 10000)
    fmt.Printf("Subtracting 1000 nanosecond:\n %s\n", newT)

    //Add 1 year 2 month 4 day
    newT = t.AddDate(-1, -2, -4)
    fmt.Printf("Subtracting 1 year 2 month 4 day:\n %s\n", newT)
}

Output:

Subtracting 1 hour:
 2020-02-01 00:18:29.772673 +0530 IST m=-3599.999784391

Subtracting 15 minute:
 2020-02-01 01:03:29.772673 +0530 IST m=-899.999784391

Subtracting 10 sec:
 2020-02-01 01:18:19.772673 +0530 IST m=-9.999784391

Subtracting 100 millisecond:
 2020-02-01 01:18:29.762673 +0530 IST m=-0.009784391

Subtracting 1000 microsecond:
 2020-02-01 01:18:29.762673 +0530 IST m=-0.009784391

Subtracting 1000 nanosecond:
 2020-02-01 01:18:29.772663 +0530 IST m=+0.000205609

Subtracting 1 year 2 month 4 day:
 2018-11-27 01:18:29.772673 +0530 IST

Time Parsing/Formatting

If you have worked with time/date formatting/parsing in other languages you might have noticed that the other languages use special placeholders for time/date formatting. For eg ruby language uses

  • %d for day
  • %Y for year

etc

Golang, instead of using codes such as above, uses date and time format placeholders that look like date and time only. Go uses standard time, which is:

Mon Jan 2 15:04:05 MST 2006  (MST is GMT-0700)
or 
01/02 03:04:05PM '06 -0700


So if you notice go uses

  • 01 for day of the month ,
  • 02 for the month
  • 03 for hours ,
  • 04 for minutes
  • 05 for second
  • and so on

Below placeholder table describes the exact mapping. Go takes a more pragmatic approach where you don’t need to remember or lookup for the traditional formatting codes as in other languages

TypePlaceholder
Day2 or 02 or _2
Day of WeekMonday or Mon
Month01 or 1 or Jan or January
Year2006 or 06
Hour03 or 3 or 15
Minutes04 or 4
Seconds05 or 5
Milli Seconds  (ms).000        //Trailing zero will be includedor .999   //Trailing zero will be omitted
Micro Seconds (μs).000000             //Trailing zero will be includedor .999999        //Trailing zero will be omitted
Nano Seconds (ns).000000000        //Trailing zero will be includedor .999999999 //Trailing zero will be omitted
am/pmPM or pm
TimezoneMST
Timezone offset Z0700 or Z070000 or Z07 or Z07:00 or Z07:00:00  or -0700 or  -070000 or -07 or -07:00 or -07:00:00

Time Parse Example

Now coming back to time.Parse. The signature of the function is

func Parse(layout, value string) (Time, error)

time.Parse function takes in two arguments

  • First argument is the layout consisting of time format placeholder
  • Second argument is the actual formatted string representing a time.

The way you have to go about this is to make sure that the layout string (first argument ) matches the string representation (second argument) of the time you want to parse into time.Time. For parsing

  • For parsing 2020-01-29, layout string should be 06-01-02 or 2006-01-02 or something which maps correctly based on above placeholder table.
  • Similarly for parsing “2020-Jan-29 Wednesday 12:19:25” the layout string can be “2006-Jan-02 Monday 03:04:05”

Below are the working Code Examples of time.Parse().

package main

import (
    "fmt"
    "time"
)

func main() {
    //Parse YYYY-MM-DD
    timeT, _ := time.Parse("2006-01-02", "2020-01-29")
    fmt.Println(timeT)

    //Parse YY-MM-DD
    timeT, _ = time.Parse("06-01-02", "20-01-29")
    fmt.Println(timeT)

    //Parse YYYY-#{MonthName}-DD
    timeT, _ = time.Parse("2006-Jan-02", "2020-Jan-29")
    fmt.Println(timeT)

    //Parse YYYY-#{MonthName}-DD WeekDay HH:MM:SS
    timeT, _ = time.Parse("2006-Jan-02 Monday 03:04:05", "2020-Jan-29 Wednesday 12:19:25")
    fmt.Println(timeT)

    //Parse YYYY-#{MonthName}-DD WeekDay HH:MM:SS PM Timezone TimezoneOffset
    timeT, _ = time.Parse("2006-Jan-02 Monday 03:04:05 PM MST -07:00", "2020-Jan-29 Wednesday 12:19:25 AM IST +05:30")
    fmt.Println(timeT)
}

Output:

2020-01-29 00:00:00 +0000 UTC
2020-01-29 00:00:00 +0000 UTC
2020-01-29 00:00:00 +0000 UTC
2020-01-29 12:19:25 +0000 UTC
2020-01-29 00:19:25 +0530 IST

Time Formatting Example

time.Format function can be used to format time to a string representation. The signature of the function is

func (t Time) Format(layout string)

Let’s see some time format code examples

package main

import (
    "fmt"
    "time"
)

func main() {
    now := time.Now()
    
    //Format YYYY-MM-DD
    fmt.Printf("YYYY-MM-DD: %s\n", now.Format("2006-01-02"))

    //Format YY-MM-DD
    fmt.Printf("YY-MM-DD: %s\n", now.Format("06-01-02"))

    //Format YYYY-#{MonthName}-DD
    fmt.Printf("YYYY-#{MonthName}-DD: %s\n", now.Format("2006-Jan-02"))

    //Format HH:MM:SS
    fmt.Printf("HH:MM:SS: %s\n", now.Format("03:04:05"))

    //Format HH:MM:SS Millisecond
    fmt.Printf("HH:MM:SS Millisecond: %s\n", now.Format("03:04:05 .999"))

    //Format YYYY-#{MonthName}-DD WeekDay HH:MM:SS
    fmt.Printf("YYYY-#{MonthName}-DD WeekDay HH:MM:SS: %s\n", now.Format("2006-Jan-02 Monday 03:04:05"))

    //Format YYYY-#{MonthName}-DD WeekDay HH:MM:SS PM Timezone TimezoneOffset
    fmt.Printf("YYYY-#{MonthName}-DD WeekDay HH:MM:SS PM Timezone TimezoneOffset: %s\n", now.Format("2006-Jan-02 Monday 03:04:05 PM MST -07:00"))
}

Output:

YYYY-MM-DD: 2020-01-25
YY-MM-DD: 20-01-25
YYYY-#{MonthName}-DD: 2020-Jan-25
HH:MM:SS: 11:14:16
HH:MM:SS Millisecond: 11:14:16 .213
YYYY-#{MonthName}-DD WeekDay HH:MM:SS: 2020-Jan-25 Saturday 11:14:16
YYYY-#{MonthName}-DD WeekDay HH:MM:SS PM Timezone TimezoneOffset: 2020-Jan-25 Saturday 11:14:16 PM IST +05:30

Time Diff

time package has a method Sub which can be used to get the difference between two different time values. The signature of the function is

func (t Time) Sub(u Time) Duration
currentTime := time.Now()
oldTime := time.Date(2020, 1, 2, 0, 0, 0, 0, time.UTC)
diff := currentTime.Sub(oldTime)

Time Conversion

Below code shows conversion of

  • time.Time to Unix Timestamp
  • Unix Timestamp to time.Time
package main

import (
    "fmt"
    "time"
)

func main() {
    tNow := time.Now()

    //time.Time to Unix Timestamp
    tUnix := tNow.Unix()
    fmt.Printf("timeUnix %d\n", tUnix)

    //Unix Timestamp to time.Time
    timeT := time.Unix(tUnix, 0)
    fmt.Printf("time.Time: %s\n", timeT)
}

Output:

timeUnix 1257894000
time.Time: 2009-11-10 23:00:00 +0000 UTC

Convert time between different timezones

The In function can be used to change the location associated with a particular time.Time object. Whenever the In function is called on any time.Time object (say t)  then,

  • A copy of t is created representing the same time instant.
  • t’s location is set to the location passed to In function for display purposes
  • t is returned back

Let’s see the below working code which can be used to change the location value associated with a particular time.

package main

import (
    "fmt"
    "time"
)

func main() {
    now := time.Now()

    loc, _ := time.LoadLocation("UTC")
    fmt.Printf("UTC Time: %s\n", now.In(loc))
   
    loc, _ = time.LoadLocation("Europe/Berlin")
    fmt.Printf("Berlin Time: %s\n", now.In(loc))

    loc, _ = time.LoadLocation("America/New_York")
    fmt.Printf("New York Time: %s\n", now.In(loc))

    loc, _ = time.LoadLocation("Asia/Dubai")
    fmt.Printf("Dubai Time: %s\n", now.In(loc))
}

Output:

UTC Time: 2020-01-31 18:09:41.705858 +0000 UTC
Berlin Time: 2020-01-31 19:09:41.705858 +0100 CET
New York Time: 2020-01-31 13:09:41.705858 -0500 EST
Dubai Time: 2020-01-31 22:09:41.705858 +0400 +04

The post Understanding time and date in Go (Golang) – Complete Guide appeared first on Welcome To Golang By Example.

]]>
https://vikasboss.github.io/all-about-time-and-date-golang/feed/ 1 1292