29 Apr 2024 ~ 9 min read
GoLang Strings for TypeScript Developers
A quick guide to GoLang strings for TypeScript developers.
Listen to something with me 😍
Want to listen somewhere else? 🎧Contents
- Introduction
- Comparing Strings in GoLang & TypeScript
- Type of String Representations in GoLang
- String Operations
- Advanced String Operations
- References
Introduction
GoLang is a statically typed, compiled language that is syntactically similar to C, with the added benefit of memory safety, garbage collection, structural typing, and CSP-style concurrency. It is a great language for building web servers, APIs, and CLI tools. If you are coming from a TypeScript background, you might find GoLang’s string handling a bit different. I hope I can help you understand a bit more of GoLang strings with this article, outlining the differences I found confusing when I started learning GoLang.
Note: This article assumes you have some knowledge of TypeScript, and when refering to TypeScript Strings we’re referring to JavaScript strings.
Comparing Strings in GoLang & TypeScript
A strings is a sequence of characters. Both in GoLang and TypeScript, are a representation of a sequence of characters. However, GoLang strings are literally a slice of bytes.
Slice(GoLang): A slice is a reference to a contiguous segment of an array. Slices are dynamic, they can grow and shrink as needed.
In TypeScript, a string is just a sequence of characters. It is a primitive data type and is immutable. There is no distinction between a character and a string in TypeScript. Thus, when you’re manipulating strings in a TypeScript application you’re always going to be working with a string. In GoLang however, you’re going to encounter different data types depending on the operation you’re performing.
Let’s look at a simple TypeScript example:
let str = "Hello, World!";
console.log(str[0], typeof str[0]); // output: H string
console.log(str.slice(1, 4), typeof str.slice(1, 4)); // output: ell string
console.log(str, typeof str); // output: Hello, World! string
Notice how each time we check the typeof the result, it’s always a string. This is because TypeScript strings are always strings. Nothing special about them. However, in GoLang…
var cp []byte
str := "Hello, World!"
c := str[0]
partOfStr := str[0:5]
cp = []byte(str[0:5])
fmt.Println(reflect.TypeOf(str))
fmt.Println(reflect.TypeOf(partOfStr).Kind())
fmt.Println(reflect.TypeOf(c))
fmt.Println(reflect.TypeOf(cp))
This is however the first part of the revelation. There is another angle to this, strings in GoLang what you see is not what you get. Let me explain, using examples
TypeScript Example
let newline = "\n"
let newline_char = "\\n"
let literal_nl = `\n`
let literal_nl_character = `\\n`
let str_char = '\n'
let str_char_literal = '\\n'
console.log("Newline: ", newline)
console.log("Newline char: ", newline_char)
console.log("Literal String, newline", literal_nl)
console.log("Literal String, newline character", literal_nl_character)
console.log("Character, newline", str_char)
console.log("Character, newline character", str_char_literal)
// Output
Newline:
Newline char: \n
Literal String, newline:
↩︎
Literal String, newline character \n
Character, newline:
↩︎
Character, newline character: \n
GoLang Example
newline := "\n"
newline_escaped := "\\n"
literal_nl_character := `\n`
literal_not_nl := `\\n`
literal_nl := `
`
newline_char := '\n'
// newline_char_error := '\\n' // Error: illegal rune literal
println("Newline: ", newline)
println("Newline Character: ", newline_escaped)
println("Literal String, newline character: ", literal_nl_character)
println("Literal String, not newline char", literal_not_nl)
println("Literal String, actial newline", literal_nl)
println("Character, newline", newline_char)
// Output
Newline:
↩︎
Newline Character: \n
Literal Newline: \n
Literal Not Newline: \\n
Literal Newline:
↩︎
Character, newline: 10
Note: the
newline_char_error
line will throw an error because the value\\n
is not a valid rune. while\\
is a valid rune,\\n
is not a valid rune since\\
andn
are two separate characters.
The newline character is represented by the ASCII value 10. This is why the output of
newline_char
is10
.
Notice the difference? when you’re using strings, you might be getting a different result depending on the syntax you’re using to represent the string.
Type of String Representations in GoLang
A String in GoLang is a single data type, but
Rune
Let’s start with runes, A rune is a single code unit that represents a single character in GoLang. It is also an alias
for int32
and is used to represent a Unicode code point.
A code point, just means a number that represents a character. For example, the code point for the letter
a
is 97. apparently, character is ambiguous, so we use code point to refer to the number that represents a character.
rune := 'a'
fmt.Println(reflect.TypeOf(rune)) // output: int32
String
A string in GoLang is a sequence of bytes. It is a slice of bytes, also known as int8.
str := "Hello, World"
fmt.Println(reflect.TypeOf(str)) // output: string
String Literal
A string literal is the exact UTF-8 representation of the input string. What you see is what you get, meaning what you write in your editor is what you are storing in memory. This works because the GoLang compiler only accepts UTF-8 encoded source code, so it can safely store the string literal as is. Just dump what you’re writing in the string literal into memory.
str_literal := `⚡︎`
fmt.Println(reflect.TypeOf(str_literal)) // output: string
fmt.Println(str_literal) // output: ⚡︎
Note: I just placed a lightning bolt emoji in the string literal, and it was printed as is on the output. This is because the GoLang compiler only accepts UTF-8 encoded source code. Thus, can safely store the string literal as is.
String Operations
Let’s compare string operations between TypeScript and GoLang. I’ll demonstrate using simple examples
String Concatenation
TypeScript
const oldstr = "old1", oldstr2 = "old2"
const newstr = oldstr1 + oldstr2
GoLang
oldstr1 := "old1"
oldstr2 := "old2"
newstr := oldstr1 + oldstr2
newstr2 := strings.Join([]string{oldstr1, oldstr2}, "")
println("1", newstr)
println("2", newstr2)
String Length
TypeScript
const str = "Hello, World!"
console.log(str.length) // 13
GoLang
str := "Hello, World!"
println(len(str)) // 13
String Indexing, Slicing and Searching
Let’s see how typescript & golang handle string indexing, slicing and searching operations.
TypeScript
const str = "Hello, World!";
// Indexing
console.log(str.at(0)) // H
console.log(str[0]) // H
console.log(str[14]) // undefined
// Slicing
console.log(str.slice(-6)) // World!
console.log(str.substring(0, 5)) // Hello
// Searching
console.log(str.indexOf("World")) // 7
console.log(str.lastIndexOf("o")) // 8
console.log(str.includes("World")) // true
console.log(str.includes("something")) // false
console.log(str.endsWith("!")) // true
console.log(str.startsWith("a")) // false
// Matching
const str2 = "food, good, mood, wood, rude, dude, water, boot, foot"
console.log(str2.match(/.?ood/g)) // [ 'food', 'good', 'mood', 'wood' ]
console.log(str2.match(/.?oot/g)) // [ 'boot', 'foot' ]
GoLang
package main
import (
"fmt"
"regexp"
"strings"
)
func main() {
str := "Hello, World!"
// Indexing
println(str[0])
if len(str) >= 14 {
// panics without the size check
println(str[14])
} else {
println("str doesn't contain 14 bytes")
}
// Slicing
println(str[0:5]) // Hello
// println(str[-4]) // ERROR: must be uint, non-negative integer
println(str[7:]) // World!
// Searching
println(strings.Index(str, "World")) // 7
println(strings.LastIndex(str, "o")) // 8
println(strings.Contains(str, "World")) // true
println(strings.HasSuffix(str, "!")) // true
println(strings.HasPrefix(str, "a")) // false
// Matching
str2 := "find, feed, found, food, frown"
re := regexp.MustCompile(`f.?.?d`)
fmt.Printf("%q", re.FindAll([]byte(str2), -1))
}
Advanced String Operations
String Formatting
String formatting is important in GoLang for multiple reasons, one of which is for converting the string into different types of values. Let’s see how we can format strings in GoLang.
package main
import (
"fmt"
"strconv"
)
func main() {
str := "Hello, World!"
num := 10
floatNum := 10.5
// String to Int
numStr := "10"
numInt, _ := strconv.Atoi(numStr)
fmt.Println(numInt)
// Int to String
numStr2 := strconv.Itoa(num)
fmt.Println(numStr2)
// Float to String
floatStr := strconv.FormatFloat(floatNum, 'f', 2, 64)
fmt.Println(floatStr)
// String to Float
floatStr2 := "10.5"
floatNum2, _ := strconv.ParseFloat(floatStr2, 64)
fmt.Println(floatNum2)
}
More importantly formatting can be useful when you want to print out a string in a specific format. For example, you
might want to print out a string with a specific number of decimal places. This is where the fmt
package comes in
package main
import "fmt"
func main() {
str := "hello"
bty := []byte(str)
uniStr := `⚡︎`
fmt.Printf("TypeOf: %v %T\n", string(str), str) // TypeOf: h uint8
fmt.Printf("TypeOf: %v %T\n", string(str[0]), str[0]) // TypeOf: h uint8
fmt.Printf("TypeOf: %v %T\n", bty, bty) // TypeOf: [104 101 108 108 111] []uint8
fmt.Printf("TypeOf: %v %T\n", uniStr, uniStr) // TypeOf: ⚡︎ string
fmt.Printf("TypeOf: %x %T\n", uniStr, uniStr) // TypeOf: e29aa1efb88e string
// We can also return the string instead of sending it to an IO stream
// with the fmt.Sprintf function
str2 := fmt.Sprintf("TypeOf (Returned): %v %T\n", string(str), str)
println(str2) // TypeOf (Returned): h uint8
}
As I’ve tried to demonstrate the fmt
package is very helpful package and important if you’re looking for a way to
output string data.
String Splitting
Splitting strings can be done using two types methods, strings.Split
and strings.Cut
. There is a slight difference
between the two, strings.Split
does one thing it takes a separator and returns a []string
extracted from the input
string. While strings.Cut
returns string before and after the separator, similar to slicing the input string in two.
package main
import "strings"
String Replacing
Conclusion
I’ve tried to put together all I have experienced about string processing in golang, keep in mind I am primarily a JavaScript developer. So if you find something interesting that I didn’t point out or a suggestion on improvements from the techniques from this article please add a comment to the article, much appreciated. Thank you for reading this article.
References
- GoLang Strings, go.dev
- GoLang Slice, go.dev
- JavaScript Strings, **MDN
- GoLang RegExp Web Docs**