Last active
February 19, 2025 14:51
-
-
Save whatafunc/5304f6affce05772d71c514c26f2b95c to your computer and use it in GitHub Desktop.
Golang vs PHP
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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. | |
*/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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. | |
*/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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 | |
*/ | |
?> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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. | |
*/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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 | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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? π | |
*/ | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
π 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! | |
*/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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. | |
*/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. | |
*/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. | |
*/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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. | |
*/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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. | |
*/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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. | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Having fun with comparing Goland vs PHP