Go, Rust and PHP: The 32 differences

I couldn’t resist the temptation to pick Go and see how it’s different from PHP and Rust

Go is fast, but Rust is faster

1. Go is static and (mostly) safe

func sumSomething(number int) int {
return number + 10
}
func numberFruit(fruit Fruit) (int, fruit) {
return 10, fruit
}
func sum(number int) int {
return number + 10
}
func main() {
// This won't fly on Go as it's considered a string
fmt.Println(sum("10"))
}
func sum(sum int) int {
return sum + 10
}
func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return
}

2. The documentation is also awesome

Image for post
Image for post
The Go Playground testing a simple “Hello, world!” style of output.
Image for post
Image for post
The official “Go Tour” where most of the language concepts are explained in a interactive way.

3. Installing Go is braindead easy

Image for post
Image for post

4. Variables are… variables!

func doSomething() {
var favorite_number int
favorite_number = 10
other_number := 33
}
var number int = 10  // <-- Here it's declared.
var number string = "hello" // <-- This won't compilation error.
{
var number int = 20 // <-- This works in this scope.
}

5. Strict constants and no static

func main() {
// This works:
const A_STRING string = "Hello world"
// This doesn't even compile
const AN_ARRAY_OF_STRINGS = [2]string{"Hello", "world"}
}

6. Dude, where is my static?

var global_variable = "Hello World"const GLOBAL_STRING = "This is constant"func main() {
// ... very important stuff
fmt.Println(global_variable, " and ", GLOBAL_STRING)
}

7. Everything is passed-by-value

func sum(number int) int {
number += 10
return number
}
func main() {
number := 10
fmt.Println(sum(number)) // This will output "20"
fmt.Println(number) // But this, will output "10"
}
func sum(number *int) int {
*number += 10 // Get the value under the pointer and update it.
return *number // Return the value under the pointer.
}
func main() {
number := 10
fmt.Println(sum(&number)) // This will output "20".
fmt.Println(number) // This outputs "20" because we updated it.
fmt.Println(&number) // Prints the memory address like "0x...".
}

8. Garbage Collector done… right?

Image for post
Image for post

9. Don’t think about the Stack or the Heap

Image for post
Image for post
Go deciding that a integer goes to the stack, while other goes to the heap.

10. No threads, no async, just Go

This pings different sites by calling a function as a Goroutine. The WaitGroup allows to wait for all these to end their execution.
Image for post
Image for post
You won’t need to push routines to threads. Go will do that for you.

11. Still not the arrays you’re looking for

$mylist = ["foo", new Bar, 3]; // This is valid.
var list := [2]string{"Hello", "world!"}
var thisMap = map[string]string{
"greeting": "Hello",
"subject": "world!",
}
func main() {
var list := []interface{}{1, 2, "apple", true}
fmt.Println(arr)
// however, now you need to use type assertion access elements
i := arr[0].(int)
fmt.Printf("i: %d, i type: %T\n", i, i)
s := arr[2].(string)
fmt.Printf("b: %s, i type: %T\n", s, s)
}

12. Not your OOP language either

type Fruit struct {
family: string,
name: string,
}
func (fruit Fruit) setFamily(family string) {
fruit.family = family
}

13. Newtypes in, Tuples and Enums out

type UserName stringfunc whatIsYourName(un UserName) {
fmt.Println(un)
}
type Color int

const (
Red Color = iota // Make each constant an integer
Green
Blue
)
func main() {
var color Color = Red
if (color == Red) {
fmt.Println("The color is: ", color)
}
}
func returnsFooAndBar() (int, int) {
return 1, 2
}
foo, bar := returnsFooAndBar()

14. There is no “static methods”

class Equation {
protected $color = "transparent";
public static function new()
{
return new Equation;
}
}
$equation = Equation::new();
struct CarChassis {
color: String
}
impl CarChassis {
pub fn new() -> Self {
CarChassis {
color: "transparent"
}
}
}
type CarChassis struct {
color: string
}
func NewCarChassis() *CarChassis {
return CarChassis {
color: "transparent"
}
}
--- Later in the codevar car = NewCarChassis()

15. Functions have variadic parameters

func compare_cars(cars ...Car) bool {
// ...
}
func main() {
// ...
compare_cars(car_1, car_2, car_3)
}

16. Defer is magic

func write_something(file File, text string) {
defer file.Unlock()
// If the file cannot be locked, the program panics, but the
// deferred `file.Unlock()` will be called nonetheless.
file.Lock()
// And if we cannot append the text, it will also be called.
file.Append(string)
}
public function readFile($file) {  
try {
return $this->filesystem->read_contents($file);
} finally {
// This will be executed even if no exception is returned.
$this->cleanup($file);
}
}

17. My old friends “if”, “for” and “switch”

if roses == "red" {
fmt.Println("Roses are red")
} else {
fmt.Println("Roses are not red")
}
if _, ok := flower.(Rose); ok && roses == "red" {
fmt.Println("Yes, this is a rose and it's red")
}
switch v {
case "red":
fmt.Printf("This is a red rose")
case "blue":
fmt.Printf("This is a blue rose")
default:
fmt.Printf("This is not a red or blue rose!")
}
switch v := i.(type) {
case Rose:
fmt.Printf("This is a Rose")
case Violet:
fmt.Printf("This is a Violet")
default:
fmt.Printf("This may be not a flower at all!")
}
for i =: 0; i < 10; i++ {
fmt.Println("The number is: ", i)
}
for isSystemReady() {
fmt.Println("The system is not ready... yet")
}
for {
fmt.Prinln("I can't get out!")
}
colors := []string{"Red", "Green", "Blue"}for index, color := range colors {
fmt.Println("The color is: ", color)
}

18. There is no Iterable type

19. Nil is not a type, but a value

var number []int              // number   == nil -> true
var list map[string]string // list == nil -> true
var pointer *int // pointer == nil -> true
var channel chan int // channel == nil -> true
var function func() // function == nil -> true
type Something struct {
pointer *int
}
var something Something // Something.pointer == nil -> true
var anyType interface{} // anyType == nil -> true
var anything = nil // compile error: use of untyped nil

20. Interfaces are like ducks

type Duck interface {
Looks() string
Swims() string
Quacks() string
}
type Animal struct {}func (a Animal) Looks() string {
return "duck"
}
func (a Animal) Swims() string {
return "duck"
}
func (a Animal) Quacks() string {
return "duck"
}

If it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck.

type Thing struct {}func (t Thing) String() string {
return "Hello world!"
}
func main() {
fmt.Println(new(Thing)) // <-- "Hello world!"
}
type Duck interface {
swims() string
}

21. Empty Interfaces. You read that right.

func receive(anything interface{}) {
// ... What type is "anything"???
}

22. Interfaces can have nil values, and that’s evil

Image for post
Image for post
type SomethingInterface interface {
DoAnything() string
}
type Box struct{}func (box *Box) DoAnything() string {
return "I did something!"
}
func isThisNil(value interface{}) {
if value == nil { // <-- This will be false!
fmt.Println("The box is nil")
} else {
fmt.Println("The box is NOT nil") // <-- And this will run!
}
}
func main() {
// Here we got the pointer of Box, with no data.
var box *Box = nil
// This says the pointer implements an interface, and it's valid
var pointer_of_box SomethingInterface = box
// Pass the pointer to the function.
// The function will get this as valid, and execute
isThisNil(pointer_of_box)
}

23. Public or Private, nothing in between

type Fruit struct { // <-- This is public
family: string,
name: string,
}
func (fruit Fruit) setFamily(family string) { // <-- This is private
fruit.family = family
}
func (fruit Fruit) WhatFamilyIs() string { // <-- This is public
return fruit.family
}
import ( "fmt", "fruits" )func main() {
fruit := Fruit { family: "Apple" }
fmt.Println(fruit.WhatFamilyIs()) // <-- This will work

fruit.setFamily("Pineapple") // <-- But this won't.
}

24. No traits whatsoever

trait Barks()
{
public function bark() {
return property_exists($this, 'bark_sound') ?
$this->bark_sound : "woof!";
}
}
trait Barks {
fn bark(&self) -> String {
self.bark_sound
}
}

25. Ok, error. Panic.

function AreYouOkay(number int) (int, bool) {
// Sum 3, and return false if the number was below 3
number += 3
return number, number > 3
}
func main() {
number, ok := AreYouOkay(10)
// If the number is not okay, print this next string line.
if !ok {
fmt.Println("Note the number is over 3")
}
}
import (
"errors"
)
func ThisMayFail(number int) (int, error) {
if int == 0 {
return 0, errors.New("You cannot use zero as number")
}
return number + 3, nil
}
func main() {
number, error = ThisMayFail(0);
if error != nil {
log.Fatal(error) // This properly terminates the program.
}
fmt.Println(number)
}
func dontFail() int {
// Ensure the call to `recover()` is executed regardless.
defer func() {
if recovered := recover(); recovered != nil {
return 0
}
}()
ThisWillPanic(); // <-- This will panic return 10;
}

26. Namespaces are packages

// app/MyApp/Something.php
<?php
namespace App\MyAppclass Something
{
// ...
}
// src/user/name.gopackage uservar PublicName = "John Doe"var privateName = "Maria Doe"
package mainimport (
"fmt"
"user/name"
)
func main() {
fmt.Println(name.PublicName)
}
import "github.com/vendor/goodies"func main() {
goodie.Name()
}

27. Decentralized dependencies

go mod init my-proyect# Or, if you want to publish it in Github
# go mod init github.com/myname/mypackage
go get github.com/my-vendor/my-package
package main

import (
env "github.com/joho/godotenv"
)

func main() {
err := env.Load()
// ...do something important
}

28. Reflection is like a tiny mirror

var pi float64 = 3.14fmt.Println("The value is: ", reflect.ValueOf(x).String())

29. Testing is much better, but not quite

func returnTen() int {
return 10
}
func TestReturnsTen(*testing.T) {
if returnTen() != 10 {
t.Errorf("Value returned is not 10.")
}
}

30. Profiling is awesome… once set up correctly

Image for post
Image for post
An example of tracing a Go application with pprof — Golang remote profiling and flamegraphs (Matowsky.com)

31. Golang likes the web, and you?

32. Only one way to database in Go

Bonus: It’s freaking hard to read

func (c *Case) SetC(m int, n string) {
c.Sum(m).Named(n)
}
func (n int) int {
return (int + 10) / 10
// In a perfect world, you would have done this:
// int.Sum(10).Divide(10)
}
fn power_of_ten() -> usize {
let mut = 10;
10.pow(5)
}

Graphic Designer graduate. Full Stack Web Developer. Retired Tech & Gaming Editor.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store