Skip to content

Instantly share code, notes, and snippets.

@whatafunc
Last active February 19, 2025 14:51
Show Gist options
  • Save whatafunc/5304f6affce05772d71c514c26f2b95c to your computer and use it in GitHub Desktop.
Save whatafunc/5304f6affce05772d71c514c26f2b95c to your computer and use it in GitHub Desktop.
Golang vs PHP
/*
Scenario: Safe Counter with Goroutines and Mutex
Imagine we have a counter that multiple goroutines are trying to increment. Without proper synchronization, we might run into race conditions where multiple goroutines try to modify the counter at the same time, leading to unexpected results.
We’ll use:
Goroutines to simulate concurrent access.
sync.Mutex to ensure safe updates to the counter.
*/
package main
import (
"fmt"
"sync"
)
// SafeCounter struct with a mutex
type SafeCounter struct {
mu sync.Mutex
value int
}
// Increment safely increases the counter
func (c *SafeCounter) Increment() {
c.mu.Lock() // Lock before updating
c.value++ // Critical section
fmt.Println(" Counter current Value: ", c.value)
c.mu.Unlock() // Unlock after updating
/// !!!! if not locked then output:
/*
Counter current Value: 9112
Counter current Value: 9113
Counter current Value: 9114
Counter current Value: 9115
Counter current Value: 9116
Counter current Value: 9117
Counter current Value: 9109
Counter current Value: 9119
Counter current Value: 9120
Nice! That output perfectly demonstrates the race conditionβ€”different goroutines are reading and
writing to value at the same time, leading to inconsistent results. Instead of always reaching 10000,
you get a lower (and unpredictable) value because some increments are getting lost due to concurrent
writes.
*/
}
// GetValue safely retrieves the counter value
func (c *SafeCounter) GetValue() int {
c.mu.Lock() // Lock before reading
defer c.mu.Unlock() // Unlock after function returns
return c.value
}
func main() {
counter := SafeCounter{} // Initialize counter
var wg sync.WaitGroup // WaitGroup to wait for goroutines
// Start 10 goroutines to increment the counter
for i := 0; i < 10; i++ {
wg.Add(1) // Add one to WaitGroup
go func() {
for j := 0; j < 1000; j++ {
counter.Increment()
}
wg.Done() // Mark goroutine as done
}()
}
wg.Wait() // Wait for all goroutines to finish
fmt.Println("Final Counter Value:", counter.GetValue()) // Expected: 10000
}
/*
Explanation
Goroutines (go func() {...})
We create 10 goroutines, each incrementing the counter 1000 times.
This simulates multiple threads accessing a shared resource.
Mutex (sync.Mutex)
mu.Lock() ensures only one goroutine can modify the value at a time.
mu.Unlock() releases the lock after updating.
WaitGroup (sync.WaitGroup)
We use WaitGroup to make sure the main function waits for all goroutines to finish before printing the final counter value.
Why Use Mutex?
If we removed the mu.Lock() and mu.Unlock(), multiple goroutines would try to update value at the same time, leading to data races and incorrect results.
Want to test it? Try removing the mutex and see what happens! πŸš€
.......
Counter current Value: 9959
Counter current Value: 9960
Counter current Value: 9961
Counter current Value: 9962
Counter current Value: 9963
Counter current Value: 9964
Counter current Value: 9965
Counter current Value: 9945
Counter current Value: 9966
Counter current Value: 9967
Counter current Value: 9968
Counter current Value: 9969
Counter current Value: 9970
Counter current Value: 9971
.....
Nice! That output perfectly demonstrates the race conditionβ€”different goroutines are reading and writing to value at the same time, leading to inconsistent results. Instead of always reaching 10000, you get a lower (and unpredictable) value because some increments are getting lost due to concurrent writes.
*/
/*
Strings in Go are immutable byte slices ([]byte)
- A Go string is a sequence of bytes, not characters.
- It defaults to UTF-8 encoding, making it easier to handle multilingual text.
UTF-8 Handling in Go
- Unlike some languages that use UTF-16 (like Java, JavaScript), Go stores strings as raw UTF-8 bytes.
- rune is used to represent a Unicode code point (since a single character may be more than one byte).
*/
package main
import "fmt"
func main() {
s := "δ½ ε₯½, δΈ–η•Œ" // UTF-8 encoded Chinese characters
fmt.Println(len(s)) // 15 bytes (not characters)
for _, r := range s {
// fmt.Printf("%c ", r) // Correctly prints each character
fmt.Printf("Character: %c, Unicode: %U, Decimal: %d\n", r, r, r) // print the Unicode code point of a character (rune), print the decimal code of a character
}
fmt.Println(s) // Full string
}
/*
πŸ”ΉComparison
[]rune (rune slice)
Fast for character access (O(1)) but wastes memory (fixed 4 bytes per character).
Better when modifying or iterating over characters frequently.
Bad for large text data (e.g., files, logs, network).
πŸ”Ή Example: Storing "HelloπŸ˜€"
Type Memory Usage
UTF-8 string 6 bytes (48 65 6C 6C 6F F0 9F 98 80)
[]rune slice 24 bytes (48 00 00 00 65 00 00 00 6C 00 00 00 6C 00 00 00 6F 00 00 00 00 F6 01 00)
βœ… UTF-8 string wins for memory efficiency!
πŸ”ΉHow to Choose Between string and []rune in Go?
Scenario Best Choice
Working with raw text, storing data βœ… string (UTF-8)
Iterating through individual characters βœ… []rune (if modifying runes)
Needing memory efficiency βœ… string (UTF-8)
Needing fixed-size storage βœ… []rune (each char = 4 bytes)
πŸ”ΉπŸ”ΉπŸ”ΉConclusionπŸ”ΉπŸ”ΉπŸ”Ή
Golang natively supports UTF-8 without needing additional functions.
*/
<?php
/*
How PHP Handles UTF-8
PHP does not use UTF-8 natively for strings. Instead:
- Strings in PHP are just sequences of bytes, like in Go.
- PHP does not assume UTF-8 encoding, meaning some functions might misinterpret multibyte characters.
*/
$newLine = "\r\n";
$s = "δ½ ε₯½, δΈ–η•Œ";
echo strlen($s).$newLine; // Outputs 14, counts bytes not characters
echo mb_strlen($s, "UTF-8").$newLine; // Outputs 6, correct number of characters
/*Unlike Go, PHP requires mbstring functions (like mb_strlen) to properly count UTF-8 characters.
String operations in PHP and UTF-8
Many default PHP functions (substr, strtoupper, etc.) do not work correctly with UTF-8.
You must use mb_ functions: The mb_ prefix in PHP stands for "Multibyte".
PHP functions with this prefix come from the Multibyte String (mbstring) extension, which is used to correctly handle UTF-8 and other multibyte character encodings.
Since PHP’s default string functions (like strlen, substr) do not properly handle multibyte characters, mbstring functions are necessary for working with Unicode text.
*/
echo substr("MΓΌnchen", 0, 3).$newLine; // Outputs: MοΏ½ (invalid UTF-8 handling)
echo mb_substr("MΓΌnchen", 0, 3, "UTF-8").$newLine; // Outputs: MΓΌn
/*
In Go, such issues don’t exist because UTF-8 is the default.
Conclusion
- Golang natively supports UTF-8 without needing additional functions.
- PHP requires mbstring functions for proper UTF-8 handling. So, if handling multilingual text, Go is more reliable because it assumes UTF-8 by default.
Comparison: string vs. []rune in Memory
πŸ”Ή String (UTF-8)
Memory-efficient: Uses only the necessary bytes.
Better for storage & network transmission.
Needs decoding when processing characters individually.
KEY Take points:
Common mb_ functions:
mb_strlen($str, "UTF-8") β†’ Correct character length
mb_substr($str, start, length, "UTF-8") β†’ UTF-8-safe substring
mb_strtoupper($str, "UTF-8") β†’ Convert to uppercase (UTF-8 safe)
mb_detect_encoding($str) β†’ Detects encoding of a string
*/
?>
/*
Is There Any Character with Byte Code 0x01 (Decimal 1)?
Yes! The Start of Header (SOH) control character has byte code 0x01 (decimal 1).
In ASCII and Unicode, U+0001 is SOH (Start of Header).
It is a non-printable control character used in old communication protocols.
Example: Print 0x01 in Go
*/
package main
import "fmt"
func main() {
ch := rune(1) // U+0001
fmt.Printf("Character: %q, Unicode: U+%04X, Decimal: %d\n", ch, ch, ch)
}
/*
1. Is "UTF-8" More Reliable in PHP Than a Limited Version Like "utf-8-arabic"?
Yes, "UTF-8" is the most reliable option for PHP applications because:
βœ… Universal Support – "UTF-8" covers all characters in all languages, while "utf-8-arabic" (or other localized UTF-8 variants) may be optimized for a specific script but might not fully support others.
βœ… Consistent Encoding – Many PHP functions (like json_encode, htmlspecialchars) expect "UTF-8", and using a variant might lead to unexpected behavior.
βœ… Better Interoperability – Databases, APIs, and modern browsers default to UTF-8. If you use "utf-8-arabic", it might cause incompatibility issues when exchanging data with systems expecting standard "UTF-8".
βœ… No Extra Configuration Needed – Using "UTF-8" ensures correct character encoding across PHP, MySQL (utf8mb4), and web pages (meta charset="UTF-8").
When to use a limited UTF-8 variant?
Rarely. Limited UTF-8 versions exist mainly for legacy applications or performance tuning in specific languages. In modern applications, "UTF-8" is always preferable.
2. How Much More Memory Does "UTF-8" Use Compared to a Limited Version?
Memory Consumption of UTF-8
UTF-8 is variable-length encoding, meaning:
ASCII characters (A-Z, a-z, 0-9, @, etc.) take 1 byte
Most European characters (Γ©, ΓΆ, ΓΌ, etc.) take 2 bytes
Arabic, Chinese, Japanese, Korean characters take 3 bytes
Rare symbols (e.g., emojis) can take 4 bytes
Comparing to a Limited Version (like utf-8-arabic):
A specialized "utf-8-arabic" might preallocate more memory for Arabic characters but won't save much space unless the app is strictly Arabic-only.
"UTF-8" dynamically adjusts byte usage, making it efficient for multilingual content.
Example (Memory Usage for "Ω…Ψ±Ψ­Ψ¨Ψ§" in UTF-8 vs ASCII)
Text Encoding Bytes per Char Total Bytes
"Hello" UTF-8 1 per char 5
"Ω…Ψ±Ψ­Ψ¨Ψ§" UTF-8 2 or 3 per char 10-15
βœ… UTF-8 is optimized – it only expands memory usage when necessary, while fixed-length encodings (like UTF-16) waste memory on ASCII text.
*/
/*
OK, and back to the term rune it's just same if I same a byte code?
If I am saying a char's bytecode is XXX then both a php dev and a golang dev would understand me ?
ChatGPT said:
1. Is a rune in Go the Same as a Byte Code?
No, a rune in Go is a Unicode code point, not a byte code.
A rune represents a character in Unicode (like A, Ξ©, δ½ ε₯½).
A byte code refers to the individual bytes that encode a character in memory.
Since UTF-8 is variable-length, a single rune can be 1 to 4 bytes long in Go.
-----
2. If I Say "A Char's Byte Code is XXX," Will Both a PHP and a Go Dev Understand?
πŸ”Ή In PHP: A "byte code" means the raw byte(s) representing a character (e.g., "ΓΆ" in UTF-8 is \xC3\xB6).
πŸ”Ή In Go: A rune is the Unicode representation, while a "byte code" is its encoding (UTF-8 bytes).
CONCLUSION:
How to Be Clear for Both PHP and Go Devs?
βœ… If you mean the character itself, say:
πŸ‘‰ "The Unicode code point is U+XXXX" (Go rune).
βœ… If you mean its raw memory encoding, say:
πŸ‘‰ "The UTF-8 byte sequence is XX XX" (PHP mb_chr, Go []byte{} array).
*/
package main
import "fmt"
func main() {
s := "âЁ" // Unicode U+00F6
//r := rune(s[0]) // First byte //// declared and not used: r
fmt.Printf("Rune: %c, Unicode: U+%04X\n", []rune(s)[1], []rune(s)[1]) //Rune: ΓΆ, Unicode: U+00F6
/* questions:
1 - why use slice of runes here?
2 - why it prints symbol itself in full?
*/
//fmt.Printf("Rune: %c, Unicode: U+%04X\n", r, r) //Rune: Γƒ, Unicode: U+00C3
fmt.Printf("Byte: %X\n", s) // UTF-8 encoded bytes always πŸ‘‰ Byte: C3B6
}
/*
In Go, each rune is stored in memory as a sequence of bytesβ€”not necessarily one rune per RAM cell. Let's break it down:
1. How Are Runes Stored in Memory?
A rune in Go represents a Unicode code point (e.g., U+1F600 for πŸ˜€).
Go stores runes as 4-byte (32-bit) values in memory when using the rune type.
But in a UTF-8 encoded string, each rune takes 1 to 4 bytes, and those bytes are stored contiguously in memory, not separately in different RAM cells.
2. Are Runes Stored in Separate RAM Cells?
🟒 If using a rune array ([]rune)
Each rune takes 4 bytes, even if the actual Unicode character needs fewer bytes.
Example: "A" (U+0041) only needs 1 byte in UTF-8, but Go's rune type will store it as 4 bytes.
Memory layout in a []rune:
| 0041 0000 | 03B1 0000 | 1F600000 |
| (A) | (Ξ±) | (πŸ˜€) |
πŸš€ Here, each rune is aligned in memory, but each uses 4 bytes.
πŸ”΄ If using a string (UTF-8 encoded)
No fixed 4-byte storage per rune. Instead, characters take 1 to 4 bytes based on UTF-8.
Example ("AΞ±πŸ˜€" in UTF-8, stored in RAM as bytes):
| 41 | CE B1 | F0 9F 98 80 |
| A | Ξ± | πŸ˜€ |
🧠 Here, the bytes are packed together, saving memory compared to []rune.
3. Can I Say "Each Rune Is Stored in Its Own RAM Cell"?
❌ No, because:
Go doesn’t use 1 rune per RAM cell. RAM cells don’t store high-level data types, only raw bytes.
UTF-8 strings store characters efficiently using 1–4 bytes contiguously, not in separate "cells."
Only []rune forces each rune to occupy 4 bytes in memory, but that’s just an array, not RAM cells.
βœ… Better way to say it:
πŸ‘‰ β€œEach symbol in Go is stored as a sequence of bytes in memory, with rune using fixed 4-byte storage and UTF-8 strings using variable-length storage.”
βœ… βœ… βœ… Here is a deeper dive into how Go manages memory for runes:βœ… βœ… βœ…
How Go Stores Runes in Memory: Deep Dive
In Go, runes and strings are stored in memory using different strategies, depending on how they are declared and used. Let's break it down step by step.
1️⃣ Runes vs. Strings in Memory
Data Type Storage in RAM Memory Usage
rune (single Unicode character) Always 4 bytes (fixed size, int32) Uses more memory for small characters
UTF-8 string (default) 1 to 4 bytes per character (efficient encoding) Uses minimal memory
[]rune (rune slice) 4 bytes per rune (same as rune) Takes more memory compared to string
2️⃣ UTF-8 Encoding: How Strings Are Stored
By default, Go stores strings as UTF-8 encoded bytes in memory. Since UTF-8 is variable-length, different characters take different amounts of space:
ASCII characters (A-Z, 0-9, etc.) β†’ 1 byte
Latin and Greek characters (Ξ±, Γ©, ΓΆ) β†’ 2 bytes
Emoji and Asian characters (πŸ˜€, δ½ , ε­—) β†’ 3-4 bytes
Example: Storing "AΞ±πŸ˜€" as a UTF-8 string
Character Unicode Code Point UTF-8 Bytes Memory (Hex)
A U+0041 1 byte 41
Ξ± U+03B1 2 bytes CE B1
πŸ˜€ U+1F600 4 bytes F0 9F 98 80
🟒 Stored in memory as:
41 CE B1 F0 9F 98 80
βœ… Efficient because it uses only as much space as needed.
3️⃣ rune Type: Fixed 4-Byte Storage
A rune in Go is an alias for int32, meaning it always takes 4 bytes in memory, even if the character could be stored in fewer bytes using UTF-8.
Example: Storing "AΞ±πŸ˜€" as a []rune
Each rune takes 4 bytes, so Go pads shorter characters with extra 00 bytes:
Character Unicode Code Point rune (4 bytes)
A U+0041 41 00 00 00
Ξ± U+03B1 B1 03 00 00
πŸ˜€ U+1F600 00 F6 01 00
🟒 Stored in memory as:
41 00 00 00 B1 03 00 00 00 F6 01 00
❌ Inefficient because it always uses 4 bytes per rune, even when not needed.
4️⃣ Comparison: string vs. []rune in Memory
πŸ”Ή String (UTF-8)
Memory-efficient: Uses only the necessary bytes.
Better for storage & network transmission.
Needs decoding when processing characters individually.
πŸ”Ή []rune (rune slice)
Fast for character access (O(1)) but wastes memory (fixed 4 bytes per character).
Better when modifying or iterating over characters frequently.
Bad for large text data (e.g., files, logs, network).
πŸ”Ή Example: Storing "HelloπŸ˜€"
Type Memory Usage
UTF-8 string 6 bytes (48 65 6C 6C 6F F0 9F 98 80)
[]rune slice 24 bytes (48 00 00 00 65 00 00 00 6C 00 00 00 6C 00 00 00 6F 00 00 00 00 F6 01 00)
βœ… UTF-8 string wins for memory efficiency!
5️⃣ How to Choose Between string and []rune in Go?
Scenario Best Choice
Working with raw text, storing data βœ… string (UTF-8)
Iterating through individual characters βœ… []rune (if modifying runes)
Needing memory efficiency βœ… string (UTF-8)
Needing fixed-size storage βœ… []rune (each char = 4 bytes)
6️⃣ How Do Go Strings Work Under the Hood?
A Go string is a struct with a pointer and length:
``type stringStruct struct {
data *byte // Pointer to UTF-8 encoded data
len int // Length of the string (in bytes)
}```
βœ… Immutable β†’ Changing a string creates a new copy in memory.
βœ… Efficient for read-heavy operations (e.g., logging, APIs).
❌ Slow for modifications β†’ Use []rune if you need mutability.
7️⃣ Summary: Key Takeaways
Concept Explanation
UTF-8 string Stores text efficiently (1-4 bytes per char).
rune A Unicode code point stored as int32 (always 4 bytes).
[]rune slice Uses 4 bytes per character, good for modifications but wastes memory.
Go string struct Stores a pointer + length, immutable.
Best practice Use UTF-8 string for storage, []rune for iteration.
Would you like a hands-on example comparing memory usage in Go? 😊
Yes, please go ahead with example and
also for your A Unicode code point stored as int32 (always 4 bytes). why it's int32 while it could be int8 or less?
Why Is a rune an int32 Instead of int8?
A rune in Go represents a Unicode code point, which can range from U+0000 to U+10FFFF (1,114,112 possible values). Since int8 can only store values from -128 to 127, and even uint8 (0-255) is too small, we need at least 21 bits to represent all Unicode code points. The smallest standard integer type that can store 21 bits is int32 (4 bytes, 32 bits).
βœ… Reason for int32 (rune) in Go:
Covers all Unicode characters (from U+0000 to U+10FFFF).
Provides fixed-size storage (simplifies indexing and iteration).
Avoids performance penalties from variable-length encoding.
Example: Comparing UTF-8 Strings and []rune Memory Usage
Let's compare how Go stores a UTF-8 string vs. a []rune slice.
Code Example: Measuring Memory Usage
*/
package main
import (
"fmt"
"unsafe"
)
func main() {
// Define a UTF-8 string
str := "AΞ±πŸ˜€" // 1-byte + 2-byte + 4-byte UTF-8 encoding
// Convert string to []rune (each rune is 4 bytes)
runeSlice := []rune(str)
// Print memory sizes
fmt.Printf("String (UTF-8) length: %d bytes\n", len(str))
fmt.Printf("String (UTF-8) memory size: %d bytes\n", unsafe.Sizeof(str))
fmt.Printf("Rune slice length: %d runes\n", len(runeSlice))
//fmt.Printf("Rune slice memory size: %d bytes\n", unsafe.Sizeof(runeSlice)+len(runeSlice)*4)
//invalid operation: unsafe.Sizeof(runeSlice) + len(runeSlice) * 4 (mismatched types uintptr and int)
// Print each rune with its memory representation
for i, r := range runeSlice {
fmt.Printf("Rune %d: %c (Codepoint: U+%X, Stored as: %X %X %X %X)\n",
i, r, r, byte(r>>24), byte(r>>16), byte(r>>8), byte(r))
}
/*
πŸ›  Output Explanation
For the string "AΞ±πŸ˜€":
UTF-8 encoding:
A β†’ 41 (1 byte)
Ξ± β†’ CE B1 (2 bytes)
πŸ˜€ β†’ F0 9F 98 80 (4 bytes)
Total string size: 1 + 2 + 4 = 7 bytes
[]rune encoding:
Each rune takes 4 bytes (int32 storage).
Total rune slice size: 3 * 4 = 12 bytes
πŸ“Š Memory Comparison
Data Type Size per Character Total Memory Used
UTF-8 string Variable (1-4 bytes) 7 bytes
[]rune slice Fixed 4 bytes per rune 12 bytes
πŸ”Ή Key Takeaways:
βœ… UTF-8 saves memory by using only as many bytes as needed.
❌ []rune wastes memory but provides O(1) character access (faster than decoding UTF-8).
πŸ›  Answering Your Questions
1️⃣ Why Not int8 Instead of int32 for rune?
Unicode needs 21 bits (too big for int8 or uint8).
Go prioritizes simplicity over memory optimization, so it uses int32 to represent all possible Unicode characters.
2️⃣ Which Data Type to Use?
Use Case Best Choice
Text storage, memory efficiency βœ… UTF-8 string
Character-level processing βœ… []rune (fixed-size, fast indexing)
Parsing large files βœ… UTF-8 string (less memory overhead)
Would you like a deeper dive into UTF-8 decoding or rune iteration? πŸš€
*/
}
/*
πŸ“Œ Key Takeaway: Recursive data structures like linked lists, trees, and graphs must use pointers to avoid infinite loops.
3️⃣ Example: When Using sync.Mutex for Concurrency
*/
package main
import (
"fmt"
"sync"
)
type ProductDemo struct {
Name string
Price float64
mu sync.Mutex // Mutex by value would be wrong!
}
// Function to update product price
func UpdateDemoPrice(productsDemo map[string]*ProductDemo, key string, newPrice float64) {
if product, exists := productsDemo[key]; exists {
product.mu.Lock()
product.Price = newPrice // Modify struct via pointer
product.mu.Unlock()
}
}
func main() {
productsDemo := map[string]*ProductDemo{
"P1": {Name: "Laptop", Price: 1200.50},
"P2": {Name: "Phone", Price: 699.99},
}
fmt.Println("Before update:", productsDemo["P1"])
UpdateDemoPrice(
productsDemo,
"P1",
999.99,
) // Update product price
fmt.Println("After update:", productsDemo["P1"])
fmt.Println("All products After update:", productsDemo)
}
/*
πŸ“Œ Key Takeaway: Mutexes must be passed by pointer to avoid copying locks.
πŸ”Ή So, Will the Compiler Fail?
No, Go will compile the code even if you don’t use pointers.
But if you forget to use them when necessary, your program may:
Fail logically (e.g., structs not updating).
Be inefficient (extra memory allocations).
Break concurrency (mutex deadlocks).
Cause infinite recursion (linked lists without pointers).
πŸ“Œ πŸ“Œ πŸ“Œ πŸ“Œ πŸ“Œ Golang vs PHPπŸ“Œ πŸ“Œ πŸ“Œ πŸ“Œ πŸ“Œ
In PHP, most functions return a modified value, while in Go,
we often modify values directly using pointersβ€”without needing to return anything!
That’s why in the first Go examples,
there was no return statementβ€”the function was changing the original value in-place via a pointer. πŸš€
This difference can be tricky at first, but once you get used to modifying values directly with pointers,
you'll realize how efficient and clean Go's approach is!
*/
/*
3️⃣ Example: Using Pointers with Maps for Fast Data Access
Unlike slices, maps are inherently reference types, so pointers are usually not needed. However, we might need a pointer if modifying individual struct values inside a map.
*/
package main
import "fmt"
type Product struct {
Name string
Price float64
}
// Function to update product price
func UpdatePrice(products map[string]*Product, key string, newPrice float64) {
if product, exists := products[key]; exists {
product.Price = newPrice // Modify struct via pointer
}
}
func main() {
products := map[string]*Product{
"P1": {Name: "Laptop", Price: 1200.50},
"P2": {Name: "Phone", Price: 699.99},
}
fmt.Println("Before update:", products["P1"])
UpdatePrice(products, "P1", 999.99) // Update product price
fmt.Println("After update:", products["P1"])
fmt.Println("All products After update:", products) //map[P1:0xc000010198 P2:0xc0000101b0]
}
/*
πŸ”Ή Key Takeaways
Since maps store values by reference, modifying a struct inside a map does not require the map itself to be a pointer.
However, since products["P1"] holds a *Product, we modify the struct's memory directly inside the function.
*/
package main
import "fmt"
func AddItem(slice *[]int, item int) {
*slice = append(*slice, item) // Modifies the slice at the original memory location
}
func main() {
nums := []int{1, 2, 3}
fmt.Println("Before:", nums)
AddItem(&nums, 4) // Pass the address of the slice
fmt.Println("After:", nums)
}
/*
Key Points
Since slices internally hold a reference to an array,
modifications inside functions are often reflected in the original slice.
However, passing a pointer (*[]int) ensures that even if the underlying array grows (due to append),
the changes reflect in the caller.
*/
package main
import "fmt"
type User struct {
ID int
Name string
Age int
}
// Update user name via pointer
func UpdateUserName(users *[]User, id int, newName string) {
for i := range *users {
if (*users)[i].ID == id {
(*users)[i].Name = newName
return
}
}
}
// Function to add a new user to the slice
func AddUser(users *[]User, newUser User) {
*users = append(*users, newUser) // Append modifies the original slice
}
func main() {
users := []User{
{ID: 1, Name: "Alice", Age: 25},
{ID: 2, Name: "Bob", Age: 30},
}
fmt.Println("Before update:", users)
UpdateUserName(&users, 2, "Robert")
fmt.Println("After update:", users)
AddUser(&users, User{ID: 3, Name: "Charlie", Age: 99}) // Pass slice pointer
fmt.Println("After adding:", users)
}
/*
Why Use Pointers Here?
We pass users *[]User so that modifications directly affect the original slice.
This prevents unnecessary copies, improving performance when working with large data sets.
Final Thoughts
Using pointers with structs and slices is useful for:
βœ… Efficient memory management (avoiding copies of large structs).
βœ… Modifying original values directly within functions.
βœ… Enhancing performance in data-heavy applications.
πŸ”Ή Key Takeaways
Slices are reference types (modifications inside a function usually reflect outside).
However, if append() causes a reallocation (due to slice capacity growth), passing a pointer ensures we modify the correct slice in memory.
*/
/*
4️⃣ Example: Using Pointers with a Custom Linked List
Let's create a simple linked list where each Node holds an integer and a pointer to the next Node.
*/
package main
import "fmt"
// Node represents a single element in a linked list
type Node struct {
Value int
Next *Node // Pointer to the next node
//on attempt to just use Node -> ❌ This creates an infinite recursion in memory!
}
// Function to add a node to the end of the list
func AddNode(head *Node, value int) {
current := head
for current.Next != nil {
current = current.Next // Traverse to the last node
}
current.Next = &Node{Value: value} // Create new node and link it
}
// Function to print the linked list
func PrintList(head *Node) {
current := head
for current != nil {
fmt.Print(current.Value, " -> ")
current = current.Next
}
fmt.Println("nil")
}
func main() {
head := &Node{Value: 1} // Head node
AddNode(head, 2)
AddNode(head, 3)
AddNode(head, 4)
PrintList(head) // Output: 1 -> 2 -> 3 -> 4 -> nil
}
/*
πŸ”Ή Key Takeaways
The linked list stores memory addresses, allowing efficient dynamic memory management.
Each node has a Next *Node, which acts as a pointer to the next item.
Using pointers, we avoid duplicating large data structures.
πŸ’‘ Summary: When to Use Pointers in Go?
βœ… Modify large structs efficiently (avoid unnecessary copies).
βœ… Keep slices/maps linked to the original data (especially when appending).
βœ… Manage dynamic structures (e.g., linked lists).
βœ… Pass references to avoid redundant memory allocation.
*/
<?php
/*
In PHP, function arguments are dynamically typed and passed by value by default.
*/
function changeValue($x) {
$x = 10;
return $x;
}
$y = 5;
echo changeValue($y)."\r\n";
echo $y; // Output: 5 (unchanged)
echo "\r\nπŸ’‘ In both PHP and Go, passing by value means the original variable remains unchanged. \r\n";
function changeValueByReference(&$argument) { //using & to pass by reference
$argument = 10;
}
$y = 5;
changeValueByReference($y);
echo $y; // Output: 10 (changed)
echo "\r\nπŸ’‘ done. Note: πŸ’‘ In Go, we use pointers (*int and &y) to achieve the same effect as PHP's & reference." ;
/*
PHP: Passing by Reference & Performance Benefits
Yes, in PHP, when you pass a variable by reference (&), PHP does not create a new copy of the variable in memory.
Instead, it modifies the original variable directly, which can save memory and increase speed, especially for large arrays or objects.
Example: Pass by Reference in PHP
*/
function modifyValue(&$var) { //using & to pass by reference
$var = "Changed!";
}
$value = "Original";
modifyValue($value);
echo $value; // Output: Changed!
/*
πŸ”Ή Since $value is passed by reference, the function modifies the original variable, avoiding unnecessary memory allocation.
Performance Impact of Pass by Reference
βœ… Saves memory: No need to create a copy.
βœ… Faster execution: Direct modification instead of copying large data structures.
⚠️ Risk: Unexpected side effects if modifying variables unintentionally.
*/
/*
In php for example , if I create a function - I can pass a variable into it, but what is the difference for Golang?
ChatGPT said:
You're welcome! Now, let's compare passing variables into functions in PHP vs Golang.
1. Passing Variables into Functions: PHP vs Golang
πŸ˜€In PHP, function arguments are dynamically typed and passed by value by default.
βœ…In Golang, function arguments are statically typed and also passed by value by default.
2. Example: Passing by Value
*/
package main
import "fmt"
// Define a struct
type User struct {
Name string
Age int
}
func changeValue(x int) int{
x = 10
return x
}
func changeValueByRef(x *int) {
*x = 10
}
func main() {
y := 5
changeValue(y)
fmt.Println(y) // Output: 5 (unchanged)
//πŸ’‘ In both PHP and Go, passing by value means the original variable remains unchanged.
y1 := 5
changeValueByRef(&y1) // Passing the address of y
fmt.Println(y1) // Output: 10 (changed)
//πŸ’‘ In Go, we use pointers (*int and &y) to achieve the same effect as PHP's & reference.
/*
4. Key Differences
Feature PHP Golang
Typing Dynamic ($var can change type) Static (int, string, etc.)
Pass by Value Default Default
Pass by Reference Use &$var for reference Use * (pointer)
Function Signature $var without type varName type
Memory Management Managed by PHP engine Uses explicit pointers
5. Summary
In PHP, function parameters are passed by value unless explicitly passed by reference using &.
In Golang, function parameters are always passed by value, but you can pass by reference using pointers (* and &).
Go requires explicit type declaration (int, string, etc.), whereas PHP does not require it (unless strict types are enabled).
Would you like a deeper dive into pointers in Go? πŸš€
You said:
Yes, please a bit more on go and just a question to double check on PHP so PHP can modify the variables values also by it's location in memory which also saves time & increases the speed?
ChatGPT said:
Yes, let's break it down!
Golang: Understanding Pointers for Pass by Reference
In Golang, variables are passed by value by default. If you want to modify a variable inside a function, you must use pointers.
What is a Pointer?
A pointer is a variable that stores the memory address of another variable.
Example: Passing by Value (Default Behavior) -> line 29
πŸ”Ή Since Go passes by value, y remains unchanged.
Example: Passing by Reference (Using Pointers)
To modify the original variable, we pass a pointer. -> line 34
*/
/*
Explanation
&y β†’ Gets the memory address of y.
*x = 10 β†’ Modifies the value at the memory address.
Performance Impact of Pointers in Go
βœ… Avoids unnecessary copies, useful for large structs.
βœ… Allows modification of original values.
⚠️ More complex syntax than PHP references.
Key Differences Between PHP and Go
Feature PHP (References) Go (Pointers)
Syntax &$var *var, &var
Default Passing By value By value
Reference Passing &$var modifies original Use pointers (* and &)
Performance Benefit Saves memory, avoids copies Efficient for large data
Risk Unintended side effects Nil pointer errors
When to Use Pointers in Go?
βœ… Use pointers when:
You need to modify the original variable.
You're passing a large struct to avoid unnecessary memory allocation.
🚫 Avoid pointers if:
The function doesn't need to modify the value.
The data is small (e.g., int, float, bool).
Conclusion
PHP uses & for references, which is simpler and automatic.
Golang uses explicit pointers, making memory management clearer and more controlled.
Both approaches can improve performance by avoiding unnecessary copying.
Would you like an example of using pointers with structs in Go? πŸš€
OK, sounds more interesting then I though so please do the example and also, pls do the example for a small data like variable int and float, OK?
Great! Let's break it down with two examples:
1.Using Pointers with Small Data Types (int, float)
2.Using Pointers with Structs (More Complex Data)
1️⃣ Small Data Example: int and float Pointers
In this example, we will modify an int and a float64 using pointers.
*/
// Integer example
num := 5
fmt.Println("Before:", num) // Output: 5
modifyInt(&num)
fmt.Println("After:", num) // Output: 15 (Modified)
// Float example
price := 10.5
fmt.Println("Before:", price) // Output: 10.5
modifyFloat(&price)
fmt.Println("After:", price) // Output: 15.75 (Modified)
// Using Pointers with Structs
// Let's consider a User struct. We modify the user's age using pointers.
// Creating a user
u := User{Name: "Alice", Age: 25}
fmt.Println("Before:", u) // Output: {Alice 25}
// Passing the address of struct to function
updateAge(&u, 30)
fmt.Println("After:", u) // Output: {Alice 30} (Modified)
}
// Function to modify an int value
func modifyInt(n *int) {
*n = *n + 10 // Dereferencing the pointer and modifying the value
/*1️⃣ Understanding Dereferencing (*n = *n + 10)
πŸ“Œ What is "Referencing" vs. "Dereferencing"?
Referencing (&) β†’ Getting the memory address of a variable.
Dereferencing (*) β†’ Accessing (reading or modifying) the value stored at a memory address.
*/
}
// Function to modify a float value
func modifyFloat(f *float64) {
*f = *f * 1.5 // Changing the float value
}
/*
^^^^^^^^^^^^^^
πŸ”Ή Explanation
&num β†’ Passes the memory address of num to modifyInt().
*n = *n + 10 β†’ Reads and updates the value at the memory location.
The same applies to float64 in modifyFloat().
*/
/*
2️⃣ Using Pointers with Structs
Let's consider a User struct. We modify the user's age using pointers.
\/
*/
// Function to modify user age
func updateAge(user *User, newAge int) {
user.Age = newAge // Directly modifying struct through pointer
}
/*
Explanation
&u β†’ Passes the memory address of u to updateAge().
user.Age = newAge β†’ Directly updates the struct value.
πŸš€ Key Takeaways
βœ… Using pointers with small data types (int, float): Saves memory for large-scale computations.
βœ… Using pointers with structs: Avoids copying large structs and allows direct modifications.
*/
@whatafunc
Copy link
Author

whatafunc commented Feb 17, 2025

Having fun with comparing Goland vs PHP

  1. strings
  2. runes
  3. slice of runes
  4. runes, slice of runes in regards to memory considerations
  5. vars - pointers and real values
  6. pointers to vars per structs
  7. goroutines / mutex - example of locking a counter and without locking with results output

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment