Internet Engineering
07
History · Advantages · Say Hello · Programming Elements · Analytic geometry · Packages and Modules · HTTP Server · Concurrency
Fall 2026 ·
Amirkabir University of Technology
@1995parham



Go is
Programming Language

int a[10];
a[11] = 10;
void allocate_forever(void) {
int *a = malloc(10 * sizeof(int));
a[0] = 10;
}
struct student {
int id;
char surname[255];
char forename[255];
};
#define students_foreach_loop(stds, element) \
struct students_el *element ## _el = stds->head; \
const struct student *element = element ## _el->student; \
for (; element ## _el != NULL; element ## _el = element ## _el->next, element = element ## _el ? element ## _el->student : NULL)
students_foreach_loop(stds, el) {
fprintf(fp, "Name: %s\n", el->name);
fprintf(fp, "ID: %s\n", el->id);
fprintf(fp, "\n");
}

x := 0
instead of int x = 0;).go get) and
online package documentation.select
statement.
package main
import "fmt"
func main() {
fmt.Printf("Hello, دنیا\n")
}
import declaration must follow the
package declaration.func, the name of the function, a parameter
list (empty for main), a result list (also empty here), and the body of
the function - the statements that define what it does - enclosed in
braces.Code Time.go links to see the source
codecurl to download the source code
curl https://raw.githubusercontent.com/1995parham-teaching/go-lecture/main/00-hello-world/main.go > hello-world.go
pacman -Syu goapt install golang-gosudo snap install go --classicbrew install goscoop install goMaintained precisely:
const e = 2.71828182845904523536028747135266249775724709369995957496696763
// careful: both operands are untyped *integer* constants, so this is 0
const wrong = 1 / 3
// one float operand is enough to make it a float constant
const third = 1.0 / 3
Typed or without type:
const M64 int64 = 1<<20
const M = 1<<20
Evaluated at compile-time:
const big = 1<<100 / 1e30 // valid constant expression
var
name
type =
expression
name
:=
expression
// inside functions only
Statically typed:
var x int
var s, t string
Implicitly or explicitly initialized:
var x int // x = 0
var s, t string = "foo", "bar" // multiple assignment
var x = 42 // int
var s, b = "foo", true // string, bool
Short variable declaration:
x := 42
s, b := "foo", true
x := 1
p := &x // p, of type *int, points to x
fmt.Println(*p) // 1
*p = 2 // equivalent to x = 2
fmt.Println(x) // 2
true false iota nil
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
float32 float64 complex128 complex64
bool byte rune string error
make len cap new append copy close delete
complex real imag
panic recover
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var
x := 42
y := x + 10
var c = (f - 32) * 5 / 9
var x uint8 = 1 << 1 | 1 << 5
var y uint8 = 1 << 1 | 1 << 2
fmt.Printf("%08b\n", x) // "00100010"
fmt.Printf("%08b\n", y) // "00000110"
fmt.Printf("%08b\n", x&y) // "00000010"
fmt.Printf("%08b\n", x|y) // "00100110"
fmt.Printf("%08b\n", x^y) // "00100100"
newnew(T) create an unnamed variable of type T,
initializes it to the zero value of T, and return its address, which is
a value of type *T
func newInt() *int {
return new(int)
}
// the same thing, written out
func newIntLongWay() *int {
var dummy int
return &dummy
}
// tuple assignment
a, b = b, a // swap
f, err = os.Open(filename) // multiple assignment
func gcd(x, y int) int {
for y != 0 {
x, y = y, x % y
}
return x
}
if x < y {
return x
} else {
return y
}
switch day {
case Mon:
...
// break is implicit
case Tue, Wed:
...
}
for syntax
// a traditional infinite loop
for {
// ...
}
for initialization; condition; post {
// zero or more statements
}
// a traditional "while" loop
for condition {
// ...
}
range over arrays, slices, and mapsrange produces a pair of
values: the index and the value of the element at that index.
for i, num := range numbers { ... }
for city, pop := range population { ... }
package main
import "fmt"
func Fibonacci(n int) int {
if n == 0 || n == 1 {
return 1
} else {
return Fibonacci(n-1) + Fibonacci(n-2)
}
}
func main() {
fmt.Printf("%d\n", Fibonacci(10))
}
n from the terminal first, so
type a number and press enter
please enter n: 10
is 10 prime? false
10th prime is 29
gcd 10 20 is 10
gcd 13 15 is 1
for covers the
counter, the while, and the infinite loop
package main
import "fmt"
func main() {
var n int
fmt.Scanf("%d", &n)
fmt.Printf("%d\n", n)
}
| %d | decimal integer |
| %x, %o, %b | integer in hexadecimal, octal, binary |
| %f, %g, %e | floating-point number: 3.141593 3.141592653589793 3.141593e+00 |
| %t | boolean: true or false |
| %c | rune (Unicode code point) |
| %s | string |
| %q | quoted string "abc" or rune 'c' |
| %v | any value in natural format |
| %T | type of any value |
| %% | literal percent sign (no operand) |
len function returns the number of bytes (not
runes) in a string, and the index operation s[i] retrieves
the i-th byte of string s, where 0 <= i < len(s)
s := "hello, world"
fmt.Println(len(s)) // 12
fmt.Println(s[0], s[7]) // 'h' and 'w'
s[0] = 'L' // compile error: cannot assign to s[0]
bytesstringsstrconvunicode
go doc bytes
fmt.Sprintf; another is to use the function
strconv.Itoa.strconv.FormatInt() and strconv.FormatUint can
be used to format numbers in a different basefmt.Printf verbs %b, %d,
%o, and %x are often more convenient than
Format functions
x := 123
y := fmt.Sprintf("%d", x)
fmt.Println(y, strconv.Itoa(x)) // "123 123"
strconv.Atoi or strconv.ParseInt, or
strconv.ParseUint for unsigned integersParseInt gives the size of the
integer type that the result must fit intoint64, which
you can then convert to a smaller typefmt.Sscanf is useful for parsing input that consists of
orderly mixtures of strings and number all on a single line
x, err := strconv.Atoi("123") // x is an int
y, err := strconv.ParseInt("123", 10, 64) // base 10, up to 64 bits
17 (len(s3)) != 9
[216 179 217 132 216 167 217 133 32 216 175 217 134 219 140 216 167]
72
Parham Alvani
216
179
³
[0]: س [2]: ل [4]: ا [6]: م [8]: [9]: د [11]: ن [13]: ی [15]: ا
Global string is which defined
len(s3) is
17, not 9: those letters need two bytes
each in UTF-8s3[0] is 216, half of a character, and
printing it as %c gives nonsenserange decodes runes, so the index jumps 0,
2, 4, … and each character arrives whole
var a [3]int // array of 3 integers
fmt.Println(a[0]) // print the first element
fmt.Println(a[len(a) - 1]) // print the last element, a[2]
a := [2]int{1, 2}
b := [...]int{1, 2}
c := [2]int{1, 3}
fmt.Println(a == b, a == c, b == c) // "true false false"
d := [3]int{1, 2}
fmt.Println(a == d) // compile error: cannot compare [2]int == [3]int
// Print the indices and elements.
for i, v := range a {
fmt.Printf("%d %d\n", i, v)
}
// Print the elements only.
for _, v := range a {
fmt.Printf("%d\n", v)
}
[]T // slice of T

s[i:j], where 0 <= i <=
j <= cap(s), creates a new slice that refers to elements i through
j - 1 of the sequence s.
len(s)
s[i]
s[i:j]
append(s, x) // append element x to slice s and return new slice
makeappend
s1: [10 20 30 0 0 0 0 0 0 0], len(s1): 10, cap(s1): 10
s1: [10 20 30 0 0 0 0 0 0 0 10], len(s1): 11, cap(s1): 20
s2: [10], len(s2): 1, cap(s2): 10
before appending a new variable into s
address of s is 0x76fb35a2a048
address of s[0] is 0x76fb35a280a0
after appending a new variable into s
address of s is 0x76fb35a2a048
address of s[0] is 0x76fb35a24140
&s[0] changed: anything still pointing
at the old array no longer sees the updates
package main
type Sample struct {
S1 int
S2 int
S3 string
private float64
}
func main() {
var smp Sample
smp.S1 = 10
smp.S2 = 20
smp.S3 = "Hello World"
}
S1, S2,
S3 are public and can be accessed from
anywhere.private is private and is only visible
to code in the same package.
package main
import "fmt"
type Example struct {
Val string
count int
}
// define a custom type based on go standard types
type integer int
func (i integer) log() {
fmt.Printf("%d\n", i)
}
// pointer reciever which can change 'example' fields
func (e *Example) Log() {
e.count++
fmt.Printf("%d %s\n", e.count, e.Val)
}
func main() {
var i integer
exm := Example{
Val: "Example",
count: 10}
i.log()
exm.Log()
}
==, then add a
slice field and try againString() method and print
it with Println
we can compare student structs
Name: Parham, Family: Alvani, age: 27
student, Parham Alvani
Hello Torvalds, I am Parham Alvani (27)
Println found String() and
used it, instead of printing the fields
package main
import "fmt"
type Printer interface {
Print()
}
type Foo struct {
X, Y int
}
type Bar struct {
X, Y float64
}
func (f Foo) Print() {
fmt.Printf("%d %d\n", f.X, f.Y)
}
func (b Bar) Print() {
fmt.Printf("%g %g\n", b.X, b.Y)
}
Student to a Printer,
with no "implements" anywhere, ok form, then use a type switch
Linus Torvalds
p is not a person
Hello
Student satisfies Printer just by having the
method: interfaces are
satisfied implicitlyp.(Person) without
, ok would panic; with it
you get false instead
package main
func main() {
one := 1
var f float32 = float32(one)
fmt.Println(f) // 1
}
package main
import "fmt"
type example struct {
A int
}
func main() {
j := example{A: 10}
var k any = j
fmt.Println(k.(example).A) // 10
// the comma-ok form does not panic when the type does not match
if e, ok := k.(example); ok {
fmt.Println(e.A) // 10
}
}

type Point interface {
Distance() float64
ImageOnX() float64
ImageOnY() float64
}
type Cartesian struct {
X float64
Y float64
}
func (c *Cartesian) Distance() float64 {
return math.Sqrt(c.X*c.X + c.Y*c.Y)
}
func (c *Cartesian) ImageOnX() float64 {
return c.X
}
func (c *Cartesian) ImageOnY() float64 {
return c.Y
}
type Polar struct {
R float64
Theta float64
}
func (p *Polar) Distance() float64 {
return p.R
}
func (p *Polar) ImageOnX() float64 {
return p.R * math.Cos(p.Theta)
}
func (p *Polar) ImageOnY() float64 {
return p.R * math.Sin(p.Theta)
}
func main() {
p := Polar{
R: 1,
Theta: math.Pi / 2,
}
c := Cartesian{
X: 3,
Y: 4,
}
fmt.Printf("%g %g\n", p.Distance(), c.Distance())
fmt.Printf("%g %g\n", c.ImageOnX(), c.ImageOnY())
fmt.Printf("%g %g\n", p.ImageOnX(), p.ImageOnY())
}
package main
import (
"fmt"
"math"
)
type Point interface {
Distance() float64
ImageOnX() float64
ImageOnY() float64
}
type Cartesian struct {
X float64
Y float64
}
func (c *Cartesian) Distance() float64 {
return math.Sqrt(c.X*c.X + c.Y*c.Y)
}
func (c *Cartesian) ImageOnX() float64 {
return c.X
}
func (c *Cartesian) ImageOnY() float64 {
return c.Y
}
type Polar struct {
R float64
Theta float64
}
func (p *Polar) Distance() float64 {
return p.R
}
func (p *Polar) ImageOnX() float64 {
return p.R * math.Cos(p.Theta)
}
func (p *Polar) ImageOnY() float64 {
return p.R * math.Sin(p.Theta)
}
func main() {
p := Polar{
R: 1,
Theta: math.Pi / 2,
}
c := Cartesian{
X: 3,
Y: 4,
}
fmt.Printf("%g %g\n", p.Distance(), c.Distance())
fmt.Printf("%g %g\n", c.ImageOnX(), c.ImageOnY())
fmt.Printf("%g %g\n", p.ImageOnX(), p.ImageOnY())
}

└── gopherguides
└── greet
└── greet.go
package greet
import "fmt"
func Hello() {
fmt.Println("Hello, World!")
}
└── gopherguides
└── example
└── main.go
package main
import "github.com/gopherguides/greet"
func main() {
greet.Hello()
}
go.mod file, together with
information about the module's
dependencies.golang.org/x/net contains a
package in the directory html. That
package's path is golang.org/x/net/html.net/http is in the standard library, so
there is nothing to install and nothing
to keep up to dateLearn the standard library first. When you later pick a framework, you will know exactly what it is doing for you.
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("GET /hello", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello World")
})
log.Fatal(http.ListenAndServe(":1373", mux))
}
go get, one file
type Handler interface {
ServeHTTP(w http.ResponseWriter, r *http.Request)
}
w is where you
write the response,
r is the request you
readhttp.HandlerFunc adapts a plain function
to that interface, which is why HandleFunc accepts a
function
mux.HandleFunc("GET /hello", h.Get)
mux.HandleFunc("POST /hello", h.Post)
mux.HandleFunc("GET /hello/{username}", h.User)
405 Method Not Allowed
without you writing anything{username} is a wildcard, read back with
r.PathValue("username"){path...} matches the rest of the path, and
a trailing / matches a subtree
// path: GET /hello/{username}
name := r.PathValue("username")
// query: GET /hello?hello=IE
value := r.FormValue("hello")
// body: POST /hello with application/json
var req request.Name
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
Content-Type before decoding,
with mime.ParseMediaType, so
application/json; charset=utf-8 is still accepted
enc, err := json.Marshal(fmt.Sprintf("Hello World from %s", h.From))
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
_, _ = w.Write(enc)
200, and your
WriteHeader afterwards is ignored
type Hello struct {
From string
Logger *slog.Logger
}
func (h Hello) User(w http.ResponseWriter, r *http.Request) {
h.Logger.Info("path parameter", "username", r.PathValue("username"))
w.WriteHeader(http.StatusNoContent)
}
// wiring it up
h := handler.Hello{From: "Golang", Logger: logger}
mux.HandleFunc("GET /hello/{username}", h.User)
func logging(logger *slog.Logger, next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
next.ServeHTTP(w, r)
logger.Info("request",
"method", r.Method,
"path", r.URL.Path,
"took", time.Since(start),
)
})
}
srv.Handler = logging(logger, mux)
ListenAndServe in Production
srv := &http.Server{
Addr: "0.0.0.0:1373",
Handler: mux,
ReadHeaderTimeout: 5 * time.Second,
ReadTimeout: 15 * time.Second,
WriteTimeout: 15 * time.Second,
IdleTimeout: 60 * time.Second,
}
ctx, stop := signal.NotifyContext(context.Background(),
os.Interrupt, syscall.SIGTERM)
defer stop()
go func() {
if err := srv.ListenAndServe(); err != nil &&
!errors.Is(err, http.ErrServerClosed) {
logger.Error("http server failed", "error", err.Error())
}
}()
<-ctx.Done()
shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
_ = srv.Shutdown(shutdownCtx)
Shutdown stops accepting new connections
and lets in-flight requests finishhttp.ErrServerClosed, so that one is not
an errorcurl
go run ./httpserver
$ curl -i localhost:1373/hello
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Content-Length: 25
"Hello World from Golang"
$ curl -i -X POST -H 'Content-Type: application/json' \
-d '{"name":"Parham","count":3}' localhost:1373/hello
HTTP/1.1 200 OK
Content-Type: application/json
"Hello to Parham from Golang"
$ curl -o /dev/null -w '%{http_code}\n' -X DELETE localhost:1373/hello
405
GET /hello pattern and no DELETE one, so the
ServeMux answered for us
level=INFO msg="http server listening" addr=0.0.0.0:1373
level=INFO msg="read hello from query parameter" handler=hello hello=IE
level=INFO msg="read username from path parameter" handler=hello username=parham
level=INFO msg="There is a count" handler=hello count=3
log/slog writes key-value pairs, so the
logs are greppable and machine readable without a libraryhandler=hello comes from
logger.With("handler", "hello") — set once at wiring time,
attached to every linego statement launches a function call
as a goroutine
go f()
go f(x, y, ...)
Function f is launched as 3 different goroutines, all running concurrently:
package main
import (
"fmt"
"time"
)
func f(msg string, delay time.Duration) {
for {
fmt.Println(msg)
time.Sleep(delay)
}
}
func main() {
go f("A--", 300*time.Millisecond)
go f("-B-", 500*time.Millisecond)
go f("--C", 1100*time.Millisecond)
time.Sleep(20 * time.Second)
}
chan int
chan<- string // send-only channel
<-chan T // receive-only channel
var ch chan int
ch := make(chan int) // declare and initialize with newly made channel
ch <- 1 // send value 1 on channel ch
x = <-ch // receive a value from channel ch (and assign to x)
Each goroutine sends its results via channel ch:
func f(msg string, delay time.Duration, ch chan string) {
for {
ch <- msg
time.Sleep(delay)
}
}
The main goroutine receives (and prints) all results from the same channel:
func main() {
ch := make(chan string)
go f("A--", 300*time.Millisecond, ch)
go f("-B-", 500*time.Millisecond, ch)
go f("--C", 1100*time.Millisecond, ch)
for i := 0; i < 100; i++ {
fmt.Println(i, <-ch)
}
}
