In this article we show how to work with bytes in Golang.

Go uses rune , which has type int32 , to deal with multibyte characters.

Go byte example

With the %c format verb, we print the character representation of the byte.

Because we have not set the data type explicitly, Go set a default int type.

Go string to bytes

Go bytes to string, go count bytes.

We count the number of bytes and runes of the msg string.

Go byte read file

The ioutil.ReadFile reads the specified file and returns its contents as a slice of bytes.

Go byte read binary file

Go escaped bytes.

Arbitrary character values can be encoded with backslash escapes and used in string or rune literals. Go supports a common format in which a byte is represented as \x followed by two hexadecimal values.

Go bytes functions

With Contains we check if data2 slice is a subslice of data1 .

The example joins byte slices with Join , repeats a byte slice with Repeat , and trims byte slices of the specified byte with Trim .

Go bytes.Buffer

My name is Jan Bodnar and I am a passionate programmer with many years of programming experience. I have been writing programming articles since 2007. So far, I have written over 1400 articles and 8 e-books. I have over eight years of experience in teaching programming.

Golang bytes – variable and package

Bytes are a data type in Golang. In this post, we will explore bytes as well as slices of them, the bytes package itself and what can be done with it.

What is a byte variable?

A byte in Go is simply an unsigned 8-bit integer . That means it has a limit of (0 – 255) in numerical range. In Go, a byte can represent a character from a string as well.

Creating a byte variable

The syntax for declaring a byte is really simple all is needed is to declare it as a byte variable and then use it.

Zero value of a byte

The zero value of a byte is simply zero (0).

Using bytes with strings

Bytes can be converted to and from strings in Go. This is very useful in certain cases. Here is an example of using bytes along with strings.

Golang “bytes” package

The “bytes” package implements many useful functions that can be used to manipulate byte slices. It also contains a type Buffer which is an important struct that we will be exploring later on. Here are some of the most useful functions that can be used with byte slices.

1. Compare byte slices

The Compare(b1, b2 []byte) function compares byte slices lexicographically and returns int based on the following ruleset:

2. Check if a byte slice is a subslice of other

The Contains(s, a byte[]) function is used to check if a slice is subslice of another slice. Here is an example.

3. Check if a byte slice contains the rune

The ContainsRune(b []byte, r rune) function is used to check whether a byte slice contains a rune.

4. Check for equality of byte slices

The Equal(a, b []byte) function takes two-byte slices and returns true if they are equal.

The function EqualFold checks whether two-byte slices are equal even if they are different cased.

5. The “Fields” function

The Fields function separates a byte slice by whitespace it contains.

6. The “Index” function

The index function gets the first index of occurrence of a subslice in a slice of bytes. It returns -1 if it cannot find the index.

7. Join byte slices

The join function takes an array of a byte slice and joins them with a separator that it takes as an argument.

8. The repeat function

The repeat function simply repeats the slice the number of times it gets as an argument.

9. The “Split” function

The split function uses the separator to split the string into an array of string.

10. Trimming the slice

The slice can be trimmed off anything we use by simply using the trim function. Here is the way to do that.

Go bytes.Buffer type

The buffer is a useful struct which is very efficient. The Buffer type comes with the bytes package. Here is the syntax for declaring an empty buffer.

To write into a Buffer we can use the write function like this.

The Fprintf can also be used to write into the buffer.

There are some functions that can be used with the Buffer type. This comes useful for many different scenarios. Here we are going to explore some of them.

There are multiple write functions for different types of data like strings, runes, etc in the Buffer type.

These are some of the most common use cases of bytes in Go.

Go Beyond

  • Application Design
  • Go Walkthrough

Go Walkthrough: bytes + strings

Ben Johnson

In the previous post we covered byte streams but sometimes we need to work with bounded, in-memory byte slices instead. While working with a list of bytes seems simple enough, there are a lot of edge cases and common operations that make using the bytes package worthwhile. We’re also going to lump in the strings package in this post since its API is nearly identical although for use with strings.

This post is part of a series of walkthroughs to help you understand the Go standard library better. While generated documentation provides a wealth of information, it can be difficult to understand packages in a real world context. This series aims to provide context of how standard library packages are used in every day applications. If you have questions or comments you can reach me at @benbjohnson on Twitter.

A brief aside on bytes vs strings

Rob Pike has an excellent, thorough post on strings, bytes, runes, and characters but for the sake of this post I’d like to provide more concise definitions from an application developer standpoint.

Byte slices represent a mutable, resizable, contiguous list of bytes. That’s a mouthful so let’s understand what that means.

Given a slice of bytes:

It’s mutable so you can update elements:

It’s resizable so you can shrink it or grow it:

And it’s contiguous so each byte exists one after another in memory:

Strings, on the other hand, represent an immutable , fixed-size , contiguous list of bytes. That means that you can’t update a string — you can only create new ones. This is important from a performance standpoint. In high performance code, constantly creating new strings adds a lot of load on the garbage collector.

From an application development perspective, strings tend to be easier to use when working with UTF-8 data, they can be used as map keys whereas byte slices cannot, and most APIs use strings for arguments containing character data. On the other hand, byte slices work well when you’re dealing with raw bytes such as processing byte streams. They are also good to use when you need to avoid allocations and can reuse them.

Adapting strings & slices for streams

One of the most important features of the bytes and strings packages is that it provides a way to interface in-memory byte slices and strings as io. Reader and io. Writers .

In-memory readers

Two of the most underused tools in the Go standard library are the bytes. NewReader and strings. NewReader functions:

These functions return an io. Reader implementation that wraps around your in-memory byte slice or string. But these aren’t just readers — they implement all the read-related interfaces in io including io. ReaderAt , io. WriterTo , io. ByteReader , io. ByteScanner , io. RuneReader , io. RuneScanner , & io. Seeker .

I frequently see code where byte slices or strings are written to a bytes. Buffer and then the buffer is used as a reader:

However, this approach incurs heap allocations which will be slow and use additional memory. A better option is to use the strings. Reader :

This approach also works when you have multiple strings or byte slices by using the io.MultiReader:

In-memory writer

The bytes package also includes an in-memory implementation of io. Writer called Buffer . It implements nearly all the io interfaces except io. Closer & io. Seeker . There’s also a helper method called WriteString () for writing a string to the end of the buffer.

I use Buffer extensively in unit tests for capturing log output from services. You can pass it as an argument to log. New () and then verify output later:

However, in production code, I rarely use Buffer . Despite its name, I don’t use it to buffer reads and writes since there’s a package called bufio specifically for that purpose.

Package organization

At first glance the bytes and strings packages appear large but they are really just a collection of simple helper functions. We can group them into a handful of categories:

Comparison functions

Inspection functions.

  • Prefix/suffix functions

Replacement functions

Splitting & joining functions.

Once we understand how the functions group together, the large API seems much more approachable.

When you have two byte slices or strings you may need to ask one of two questions. First, are these two objects equal? Second, which one comes before the other when sorted?

The Equal () function answers our first question:

This function only exists in the bytes package because strings can be compared with the == operator.

Although checking for equality seems easy, one common mistake is to use strings. ToUpper () to perform case-insensitive equality checks:

This is flawed because it requires 2 allocations of new strings. A better approach is to use EqualFold ():

The term “Fold” refers to Unicode case-folding . It encompasses regular uppercase & lowercase rules for A-Z as well as rules for other languages such as converting φ to ϕ.

To determine the sort order for two byte slices or strings, we’ll use Compare ():

This function returns -1 if a is less than b , 1 if a is greater than b , and 0 if a and b are equal. This function exists in the strings package only for symmetry with the bytes package. Russ Cox even calls out in the function’s comments that “ basically no one should use strings.Compare. ” Instead, use the built-in < and > operators.

“Basically no one should use strings.Compare”, Russ Cox

Typically you’ll want to know if a byte slice is less than another byte slice for the purpose of sorting. The sort. Interface requires this for its Less() function. To convert from the ternary return value of Compare () to the boolean required by Less(), we simply check for equality with -1:

The bytes & strings packages provide several ways to find data within your byte slices and strings.

If you are validating input from a user, it’s important to verify that certain bytes exist (or don’t exist). You can use the Contains () function to check for existence of one or more subslices or substrings:

For example, you may not allow input with certain off-color words:

If you need to obtain the exact number of times a subslice or substring was used, you can use Count ():

Another use for Count () is to return the number of runes in a string. By passing in an empty slice or blank string as the sep argument, Count () will return the number of runes + 1. This is different from len () which will return the number of bytes. The distinction is important when dealing with multi-byte Unicode characters:

The first line above may seem odd because there are 5 runes but remember that Count () returns the rune count plus one.

Asserting contents is important but sometimes you’ll need to find the exact position of a subslice or substring. You can do this using the index functions:

There are multiple index functions for different use cases. Index () finds a multi-byte subslice. IndexByte () finds a single byte within a slice. IndexRune () finds a unicode code-point within a UTF-8 interpreted byte slice. IndexAny () works like IndexRune () but searches for multiple code-points at the same time. Finally, IndexFunc () allows you to pass in a custom function to evaluate each rune in your byte slice until a match.

There’s also a matching set of functions for searching for the first instance of the end of a byte slice or string:

I don’t use the index functions much because I find that I typically need to build something more complex such as a parser.

Prefixing, suffixing, & trimming

Working with content at the beginning and end of a byte slice or string is a special case of inspection but it’s a important enough to warrant its own section.

Checking for prefixes & suffixes

Prefixes come up a lot in programming. For example, HTTP paths are typically grouped by functionality with common prefixes. Another example is special characters at the beginning of a string such as “@” for mentioning a user.

The HasPrefix () and HasSuffix () functions allow you to check for these situations:

These functions may seem too simple to bother with but one common mistake I see is when developers forget to check for zero length values:

This code looks simple enough but if str is blank then the program will panic. The HasPrefix () function includes this validation for you:

The term “trimming” in the bytes and strings packages refers to removing bytes or runes from the beginning and/or end of a byte slice or string. The most generic function for this is Trim ():

This will remove any runes in cutset from the beginning and end of your string. You can also trim from just the beginning or just the end of your string using TrimLeft () and TrimRight (), respectively.

But generic trimming isn’t very common. Most of the time you want to trim white space characters and you can use TrimSpace () for this:

You might think that trimming with a “ \n\t” cutset is enough but TrimSpace () will trim all Unicode defined white space. This includes not only the space, newline, and tab characters but also more unusual white space characters such as thin space or hair space .

TrimSpace () is actually just a thin wrapper around TrimFunc () which is a function for evaluating leading and trailing runes for trimming:

This makes it simple to create your own whitespace trimmer for only trailing characters:

Finally, if you want to trim exact prefixes or suffixes instead of character sets, there are the TrimPrefix () and TrimSuffix () functions:

These can go hand in hand with the HasPrefix () and HasSuffix () functions if you want to replace a prefix or suffix. For example, I use this to implement Bash-style home directory completion for paths my config files:

Basic replacement

Swapping out subslices or substrings is sometimes necessary. For the most simple cases, the Replace () function is all you need:

It swaps out any instance of old with new in your string. You can set n to a non-negative number to limit the number of replacements. This function is good if you have a simple placeholder in a user defined template. For example, you want to let users specify “$NOW” and have it replaced with the current time:

If you have multiple mappings then you’ll need to use strings. Replacer . This works by specifying old/new pairs to strings. NewReplacer ():

Case replacement

You may assume that casing is simple — upper & lower case — but Go works with Unicode and Unicode is never that simple. There are 3 types of casing: upper, lower, and title case.

Uppercase and lowercase are straight foward for most languages and you can use the ToUpper () and ToLower () functions:

However, some languages have different rules for casing. Turkish, for example, uppercases its i as İ . For these special case languages, there are special versions of these functions:

Next we have title case and the ToTitle () function:

You may be surprised, however, when you use ToTitle () and all your characters are uppercased:

That’s because in Unicode, title case is a specific type of casing and not a way to capitalize the first character in each word. For the most part, title case and upper case are the same but there are a few code points which have differences. For example, the lj code point (yes, that’s one code point) is uppercased as LJ but title cased as Lj.

What you’re probably looking for is the Title () function:

This outputs the expected result:

Mapping runes

One other function for replacing data in a bytes slice or string is Map ():

This function lets you pass in a function to evaluate every rune and replace it. Admittedly, I didn’t even know this function existed until I started writing this post so I can’t give any personal anecdote.

Many times we have delimited strings that we need to break apart. For example, paths in Unix are joined with colons and the CSV file format is essentially just fields of data delimited by commas.

Substring splitting

For simple subslice or substring splitting, we have the Split() functions:

These break up byte slices or strings by a delimiter and return the subslices or substrings. The “After” functions include the delimiter at the end of the substrings. The “N” functions limit the number of splits that can occur:

Splitting data is a very common operation, however, it’s typically done in the context of a file format such as CSV or in the context of path splitting. For these operations, I use the encoding/csv or path packages instead.

Categorical splitting

Sometimes you want to specify delimiters as a set of runes instead of a series of runes. The best example of this is breaking apart words by variable-length whitespace. Simply calling Split () using a space delimiter will give you empty substrings if you have multiple contiguous spaces. Instead you can use the Fields() function:

This will consider consecutive whitespace characters a single delimiter:

The Fields() function is just a simple wrapper around FieldsFunc() which lets you pass a function to evaluate each rune as a delimiter:

Instead of breaking apart delimited data, we can join it together using the Join() function:

One common mistake I‘ve seen is when developers try to implement join by hand. It looks something like:

The flaw in this code is that you are creating a massive number of allocations. Because strings are immutable, each iteration is generating a new string for each append. The strings. Join () function, on the other hand, uses a byte slice buffer to build upon and converts it back to a string when it returns. This minimizes heap allocations.

Miscellaneous functions

There’s two functions I couldn’t find a category for so they’re lumped in here at the bottom. First, the Repeat () function allows you generate a repeated byte slice or string. Honestly, the only time I can remember using this is to make a line to separate content in the terminal:

The other function is Runes () which returns a slice of all runes in a UTF-8 interpreted byte slice or string. I‘ve never needed to use this since the for loop over a string does the same thing but without the allocations.

Byte slices and strings are fundamental primitives in Go. They are the in-memory representations for series of bytes and runes. The bytes and strings packages provide a ton of useful helper functions as well as adapters to the io.Reader and io.Writer interfaces.

It’s easy to overlook many of the useful tools in these packages because of the API’s size but I hope this post has helped you to understand everything these packages have to offer.

Ben Johnson

Ben Johnson

Freelance Go developer, author of BoltDB

You might also like

Go walkthrough: fmt.

In the last post we looked at fast, primitive encoding using strconv [https://golang.org/pkg/strconv/] but in this post we’ll take a higher level approach by using templates with the fmt [https://golang.org/pkg/fmt/] package. The fmt [https://golang.org/pkg/fmt/] package builds on

Go Walkthrough: strconv

Formatting & parsing primitive values in Go is a common task. You probably first dipped your toes into the fmt [https://golang.org/pkg/fmt/] package when you started Go, however, there’s a less commonly used package for basic formatting that’s more efficient and preserves compiler type checking.

Featured Posts

Standard package layout.

Standard Package Layout

This package is not in the latest version of its module.

Documentation ¶

Package bytes implements functions for the manipulation of byte slices. It is analogous to the facilities of the strings package.

  • func Clone(b []byte) []byte
  • func Compare(a, b []byte) int
  • func Contains(b, subslice []byte) bool
  • func ContainsAny(b []byte, chars string) bool
  • func ContainsFunc(b []byte, f func(rune) bool) bool
  • func ContainsRune(b []byte, r rune) bool
  • func Count(s, sep []byte) int
  • func Cut(s, sep []byte) (before, after []byte, found bool)
  • func CutPrefix(s, prefix []byte) (after []byte, found bool)
  • func CutSuffix(s, suffix []byte) (before []byte, found bool)
  • func Equal(a, b []byte) bool
  • func EqualFold(s, t []byte) bool
  • func Fields(s []byte) [][]byte
  • func FieldsFunc(s []byte, f func(rune) bool) [][]byte
  • func HasPrefix(s, prefix []byte) bool
  • func HasSuffix(s, suffix []byte) bool
  • func Index(s, sep []byte) int
  • func IndexAny(s []byte, chars string) int
  • func IndexByte(b []byte, c byte) int
  • func IndexFunc(s []byte, f func(r rune) bool) int
  • func IndexRune(s []byte, r rune) int
  • func Join(s [][]byte, sep []byte) []byte
  • func LastIndex(s, sep []byte) int
  • func LastIndexAny(s []byte, chars string) int
  • func LastIndexByte(s []byte, c byte) int
  • func LastIndexFunc(s []byte, f func(r rune) bool) int
  • func Map(mapping func(r rune) rune, s []byte) []byte
  • func Repeat(b []byte, count int) []byte
  • func Replace(s, old, new []byte, n int) []byte
  • func ReplaceAll(s, old, new []byte) []byte
  • func Runes(s []byte) []rune
  • func Split(s, sep []byte) [][]byte
  • func SplitAfter(s, sep []byte) [][]byte
  • func SplitAfterN(s, sep []byte, n int) [][]byte
  • func SplitN(s, sep []byte, n int) [][]byte
  • func Title(s []byte) []byte deprecated
  • func ToLower(s []byte) []byte
  • func ToLowerSpecial(c unicode.SpecialCase, s []byte) []byte
  • func ToTitle(s []byte) []byte
  • func ToTitleSpecial(c unicode.SpecialCase, s []byte) []byte
  • func ToUpper(s []byte) []byte
  • func ToUpperSpecial(c unicode.SpecialCase, s []byte) []byte
  • func ToValidUTF8(s, replacement []byte) []byte
  • func Trim(s []byte, cutset string) []byte
  • func TrimFunc(s []byte, f func(r rune) bool) []byte
  • func TrimLeft(s []byte, cutset string) []byte
  • func TrimLeftFunc(s []byte, f func(r rune) bool) []byte
  • func TrimPrefix(s, prefix []byte) []byte
  • func TrimRight(s []byte, cutset string) []byte
  • func TrimRightFunc(s []byte, f func(r rune) bool) []byte
  • func TrimSpace(s []byte) []byte
  • func TrimSuffix(s, suffix []byte) []byte
  • type Buffer
  • func NewBuffer(buf []byte) *Buffer
  • func NewBufferString(s string) *Buffer
  • func (b *Buffer) Available() int
  • func (b *Buffer) AvailableBuffer() []byte
  • func (b *Buffer) Bytes() []byte
  • func (b *Buffer) Cap() int
  • func (b *Buffer) Grow(n int)
  • func (b *Buffer) Len() int
  • func (b *Buffer) Next(n int) []byte
  • func (b *Buffer) Read(p []byte) (n int, err error)
  • func (b *Buffer) ReadByte() (byte, error)
  • func (b *Buffer) ReadBytes(delim byte) (line []byte, err error)
  • func (b *Buffer) ReadFrom(r io.Reader) (n int64, err error)
  • func (b *Buffer) ReadRune() (r rune, size int, err error)
  • func (b *Buffer) ReadString(delim byte) (line string, err error)
  • func (b *Buffer) Reset()
  • func (b *Buffer) String() string
  • func (b *Buffer) Truncate(n int)
  • func (b *Buffer) UnreadByte() error
  • func (b *Buffer) UnreadRune() error
  • func (b *Buffer) Write(p []byte) (n int, err error)
  • func (b *Buffer) WriteByte(c byte) error
  • func (b *Buffer) WriteRune(r rune) (n int, err error)
  • func (b *Buffer) WriteString(s string) (n int, err error)
  • func (b *Buffer) WriteTo(w io.Writer) (n int64, err error)
  • type Reader
  • func NewReader(b []byte) *Reader
  • func (r *Reader) Len() int
  • func (r *Reader) Read(b []byte) (n int, err error)
  • func (r *Reader) ReadAt(b []byte, off int64) (n int, err error)
  • func (r *Reader) ReadByte() (byte, error)
  • func (r *Reader) ReadRune() (ch rune, size int, err error)
  • func (r *Reader) Reset(b []byte)
  • func (r *Reader) Seek(offset int64, whence int) (int64, error)
  • func (r *Reader) Size() int64
  • func (r *Reader) UnreadByte() error
  • func (r *Reader) UnreadRune() error
  • func (r *Reader) WriteTo(w io.Writer) (n int64, err error)
  • Buffer (Reader)
  • Buffer.AvailableBuffer
  • Buffer.Bytes
  • Buffer.Grow
  • Buffer.Next
  • Buffer.Read
  • Buffer.ReadByte
  • Compare (Search)
  • ContainsAny
  • ContainsFunc
  • ContainsRune
  • LastIndexAny
  • LastIndexByte
  • LastIndexFunc
  • SplitAfterN
  • ToLowerSpecial
  • ToTitleSpecial
  • ToUpperSpecial
  • ToValidUTF8
  • TrimLeftFunc
  • TrimRightFunc

Constants ¶

MinRead is the minimum slice size passed to a Buffer.Read call by Buffer.ReadFrom . As long as the Buffer has at least MinRead bytes beyond what is required to hold the contents of r, Buffer.ReadFrom will not grow the underlying buffer.

Variables ¶

ErrTooLarge is passed to panic if memory cannot be allocated to store data in a buffer.

Functions ¶

Func clone ¶ added in go1.20.

Clone returns a copy of b[:len(b)]. The result may have additional unused capacity. Clone(nil) returns nil.

func Compare ¶

Compare returns an integer comparing two byte slices lexicographically. The result will be 0 if a == b, -1 if a < b, and +1 if a > b. A nil argument is equivalent to an empty slice.

func Contains ¶

Contains reports whether subslice is within b.

func ContainsAny ¶ added in go1.7

ContainsAny reports whether any of the UTF-8-encoded code points in chars are within b.

func ContainsFunc ¶ added in go1.21.0

ContainsFunc reports whether any of the UTF-8-encoded code points r within b satisfy f(r).

func ContainsRune ¶ added in go1.7

ContainsRune reports whether the rune is contained in the UTF-8-encoded byte slice b.

func Count ¶

Count counts the number of non-overlapping instances of sep in s. If sep is an empty slice, Count returns 1 + the number of UTF-8-encoded code points in s.

func Cut ¶ added in go1.18

Cut slices s around the first instance of sep, returning the text before and after sep. The found result reports whether sep appears in s. If sep does not appear in s, cut returns s, nil, false.

Cut returns slices of the original slice s, not copies.

func CutPrefix ¶ added in go1.20

CutPrefix returns s without the provided leading prefix byte slice and reports whether it found the prefix. If s doesn't start with prefix, CutPrefix returns s, false. If prefix is the empty byte slice, CutPrefix returns s, true.

CutPrefix returns slices of the original slice s, not copies.

func CutSuffix ¶ added in go1.20

CutSuffix returns s without the provided ending suffix byte slice and reports whether it found the suffix. If s doesn't end with suffix, CutSuffix returns s, false. If suffix is the empty byte slice, CutSuffix returns s, true.

CutSuffix returns slices of the original slice s, not copies.

func Equal ¶

Equal reports whether a and b are the same length and contain the same bytes. A nil argument is equivalent to an empty slice.

func EqualFold ¶

EqualFold reports whether s and t, interpreted as UTF-8 strings, are equal under simple Unicode case-folding, which is a more general form of case-insensitivity.

func Fields ¶

Fields interprets s as a sequence of UTF-8-encoded code points. It splits the slice s around each instance of one or more consecutive white space characters, as defined by unicode.IsSpace , returning a slice of subslices of s or an empty slice if s contains only white space.

func FieldsFunc ¶

FieldsFunc interprets s as a sequence of UTF-8-encoded code points. It splits the slice s at each run of code points c satisfying f(c) and returns a slice of subslices of s. If all code points in s satisfy f(c), or len(s) == 0, an empty slice is returned.

FieldsFunc makes no guarantees about the order in which it calls f(c) and assumes that f always returns the same value for a given c.

func HasPrefix ¶

HasPrefix reports whether the byte slice s begins with prefix.

func HasSuffix ¶

HasSuffix reports whether the byte slice s ends with suffix.

func Index ¶

Index returns the index of the first instance of sep in s, or -1 if sep is not present in s.

func IndexAny ¶

IndexAny interprets s as a sequence of UTF-8-encoded Unicode code points. It returns the byte index of the first occurrence in s of any of the Unicode code points in chars. It returns -1 if chars is empty or if there is no code point in common.

func IndexByte ¶

IndexByte returns the index of the first instance of c in b, or -1 if c is not present in b.

func IndexFunc ¶

IndexFunc interprets s as a sequence of UTF-8-encoded code points. It returns the byte index in s of the first Unicode code point satisfying f(c), or -1 if none do.

func IndexRune ¶

IndexRune interprets s as a sequence of UTF-8-encoded code points. It returns the byte index of the first occurrence in s of the given rune. It returns -1 if rune is not present in s. If r is utf8.RuneError , it returns the first instance of any invalid UTF-8 byte sequence.

func Join ¶

Join concatenates the elements of s to create a new byte slice. The separator sep is placed between elements in the resulting slice.

func LastIndex ¶

LastIndex returns the index of the last instance of sep in s, or -1 if sep is not present in s.

func LastIndexAny ¶

LastIndexAny interprets s as a sequence of UTF-8-encoded Unicode code points. It returns the byte index of the last occurrence in s of any of the Unicode code points in chars. It returns -1 if chars is empty or if there is no code point in common.

func LastIndexByte ¶ added in go1.5

LastIndexByte returns the index of the last instance of c in s, or -1 if c is not present in s.

func LastIndexFunc ¶

LastIndexFunc interprets s as a sequence of UTF-8-encoded code points. It returns the byte index in s of the last Unicode code point satisfying f(c), or -1 if none do.

Map returns a copy of the byte slice s with all its characters modified according to the mapping function. If mapping returns a negative value, the character is dropped from the byte slice with no replacement. The characters in s and the output are interpreted as UTF-8-encoded code points.

func Repeat ¶

Repeat returns a new byte slice consisting of count copies of b.

It panics if count is negative or if the result of (len(b) * count) overflows.

func Replace ¶

Replace returns a copy of the slice s with the first n non-overlapping instances of old replaced by new. If old is empty, it matches at the beginning of the slice and after each UTF-8 sequence, yielding up to k+1 replacements for a k-rune slice. If n < 0, there is no limit on the number of replacements.

func ReplaceAll ¶ added in go1.12

ReplaceAll returns a copy of the slice s with all non-overlapping instances of old replaced by new. If old is empty, it matches at the beginning of the slice and after each UTF-8 sequence, yielding up to k+1 replacements for a k-rune slice.

func Runes ¶

Runes interprets s as a sequence of UTF-8-encoded code points. It returns a slice of runes (Unicode code points) equivalent to s.

func Split ¶

Split slices s into all subslices separated by sep and returns a slice of the subslices between those separators. If sep is empty, Split splits after each UTF-8 sequence. It is equivalent to SplitN with a count of -1.

To split around the first instance of a separator, see Cut .

func SplitAfter ¶

SplitAfter slices s into all subslices after each instance of sep and returns a slice of those subslices. If sep is empty, SplitAfter splits after each UTF-8 sequence. It is equivalent to SplitAfterN with a count of -1.

func SplitAfterN ¶

SplitAfterN slices s into subslices after each instance of sep and returns a slice of those subslices. If sep is empty, SplitAfterN splits after each UTF-8 sequence. The count determines the number of subslices to return:

  • n > 0: at most n subslices; the last subslice will be the unsplit remainder;
  • n == 0: the result is nil (zero subslices);
  • n < 0: all subslices.

func SplitN ¶

SplitN slices s into subslices separated by sep and returns a slice of the subslices between those separators. If sep is empty, SplitN splits after each UTF-8 sequence. The count determines the number of subslices to return:

func Title deprecated

Title treats s as UTF-8-encoded bytes and returns a copy with all Unicode letters that begin words mapped to their title case.

Deprecated: The rule Title uses for word boundaries does not handle Unicode punctuation properly. Use golang.org/x/text/cases instead.

func ToLower ¶

ToLower returns a copy of the byte slice s with all Unicode letters mapped to their lower case.

func ToLowerSpecial ¶

ToLowerSpecial treats s as UTF-8-encoded bytes and returns a copy with all the Unicode letters mapped to their lower case, giving priority to the special casing rules.

func ToTitle ¶

ToTitle treats s as UTF-8-encoded bytes and returns a copy with all the Unicode letters mapped to their title case.

func ToTitleSpecial ¶

ToTitleSpecial treats s as UTF-8-encoded bytes and returns a copy with all the Unicode letters mapped to their title case, giving priority to the special casing rules.

func ToUpper ¶

ToUpper returns a copy of the byte slice s with all Unicode letters mapped to their upper case.

func ToUpperSpecial ¶

ToUpperSpecial treats s as UTF-8-encoded bytes and returns a copy with all the Unicode letters mapped to their upper case, giving priority to the special casing rules.

func ToValidUTF8 ¶ added in go1.13

ToValidUTF8 treats s as UTF-8-encoded bytes and returns a copy with each run of bytes representing invalid UTF-8 replaced with the bytes in replacement, which may be empty.

func Trim ¶

Trim returns a subslice of s by slicing off all leading and trailing UTF-8-encoded code points contained in cutset.

func TrimFunc ¶

TrimFunc returns a subslice of s by slicing off all leading and trailing UTF-8-encoded code points c that satisfy f(c).

func TrimLeft ¶

TrimLeft returns a subslice of s by slicing off all leading UTF-8-encoded code points contained in cutset.

func TrimLeftFunc ¶

TrimLeftFunc treats s as UTF-8-encoded bytes and returns a subslice of s by slicing off all leading UTF-8-encoded code points c that satisfy f(c).

func TrimPrefix ¶ added in go1.1

TrimPrefix returns s without the provided leading prefix string. If s doesn't start with prefix, s is returned unchanged.

func TrimRight ¶

TrimRight returns a subslice of s by slicing off all trailing UTF-8-encoded code points that are contained in cutset.

func TrimRightFunc ¶

TrimRightFunc returns a subslice of s by slicing off all trailing UTF-8-encoded code points c that satisfy f(c).

func TrimSpace ¶

TrimSpace returns a subslice of s by slicing off all leading and trailing white space, as defined by Unicode.

func TrimSuffix ¶ added in go1.1

TrimSuffix returns s without the provided trailing suffix string. If s doesn't end with suffix, s is returned unchanged.

type Buffer ¶

A Buffer is a variable-sized buffer of bytes with Buffer.Read and Buffer.Write methods. The zero value for Buffer is an empty buffer ready to use.

func NewBuffer ¶

NewBuffer creates and initializes a new Buffer using buf as its initial contents. The new Buffer takes ownership of buf, and the caller should not use buf after this call. NewBuffer is intended to prepare a Buffer to read existing data. It can also be used to set the initial size of the internal buffer for writing. To do that, buf should have the desired capacity but a length of zero.

In most cases, new( Buffer ) (or just declaring a Buffer variable) is sufficient to initialize a Buffer .

func NewBufferString ¶

NewBufferString creates and initializes a new Buffer using string s as its initial contents. It is intended to prepare a buffer to read an existing string.

func (*Buffer) Available ¶ added in go1.21.0

Available returns how many bytes are unused in the buffer.

func (*Buffer) AvailableBuffer ¶ added in go1.21.0

AvailableBuffer returns an empty buffer with b.Available() capacity. This buffer is intended to be appended to and passed to an immediately succeeding Buffer.Write call. The buffer is only valid until the next write operation on b.

func (*Buffer) Bytes ¶

Bytes returns a slice of length b.Len() holding the unread portion of the buffer. The slice is valid for use only until the next buffer modification (that is, only until the next call to a method like Buffer.Read , Buffer.Write , Buffer.Reset , or Buffer.Truncate ). The slice aliases the buffer content at least until the next buffer modification, so immediate changes to the slice will affect the result of future reads.

func (*Buffer) Cap ¶ added in go1.5

Cap returns the capacity of the buffer's underlying byte slice, that is, the total space allocated for the buffer's data.

func (*Buffer) Grow ¶ added in go1.1

Grow grows the buffer's capacity, if necessary, to guarantee space for another n bytes. After Grow(n), at least n bytes can be written to the buffer without another allocation. If n is negative, Grow will panic. If the buffer can't grow it will panic with ErrTooLarge .

func (*Buffer) Len ¶

Len returns the number of bytes of the unread portion of the buffer; b.Len() == len(b.Bytes()).

func (*Buffer) Next ¶

Next returns a slice containing the next n bytes from the buffer, advancing the buffer as if the bytes had been returned by Buffer.Read . If there are fewer than n bytes in the buffer, Next returns the entire buffer. The slice is only valid until the next call to a read or write method.

func (*Buffer) Read ¶

Read reads the next len(p) bytes from the buffer or until the buffer is drained. The return value n is the number of bytes read. If the buffer has no data to return, err is io.EOF (unless len(p) is zero); otherwise it is nil.

func (*Buffer) ReadByte ¶

ReadByte reads and returns the next byte from the buffer. If no byte is available, it returns error io.EOF .

func (*Buffer) ReadBytes ¶

ReadBytes reads until the first occurrence of delim in the input, returning a slice containing the data up to and including the delimiter. If ReadBytes encounters an error before finding a delimiter, it returns the data read before the error and the error itself (often io.EOF ). ReadBytes returns err != nil if and only if the returned data does not end in delim.

func (*Buffer) ReadFrom ¶

ReadFrom reads data from r until EOF and appends it to the buffer, growing the buffer as needed. The return value n is the number of bytes read. Any error except io.EOF encountered during the read is also returned. If the buffer becomes too large, ReadFrom will panic with ErrTooLarge .

func (*Buffer) ReadRune ¶

ReadRune reads and returns the next UTF-8-encoded Unicode code point from the buffer. If no bytes are available, the error returned is io.EOF. If the bytes are an erroneous UTF-8 encoding, it consumes one byte and returns U+FFFD, 1.

func (*Buffer) ReadString ¶

ReadString reads until the first occurrence of delim in the input, returning a string containing the data up to and including the delimiter. If ReadString encounters an error before finding a delimiter, it returns the data read before the error and the error itself (often io.EOF ). ReadString returns err != nil if and only if the returned data does not end in delim.

func (*Buffer) Reset ¶

Reset resets the buffer to be empty, but it retains the underlying storage for use by future writes. Reset is the same as Buffer.Truncate (0).

func (*Buffer) String ¶

String returns the contents of the unread portion of the buffer as a string. If the Buffer is a nil pointer, it returns "<nil>".

To build strings more efficiently, see the strings.Builder type.

func (*Buffer) Truncate ¶

Truncate discards all but the first n unread bytes from the buffer but continues to use the same allocated storage. It panics if n is negative or greater than the length of the buffer.

func (*Buffer) UnreadByte ¶

UnreadByte unreads the last byte returned by the most recent successful read operation that read at least one byte. If a write has happened since the last read, if the last read returned an error, or if the read read zero bytes, UnreadByte returns an error.

func (*Buffer) UnreadRune ¶

UnreadRune unreads the last rune returned by Buffer.ReadRune . If the most recent read or write operation on the buffer was not a successful Buffer.ReadRune , UnreadRune returns an error. (In this regard it is stricter than Buffer.UnreadByte , which will unread the last byte from any read operation.)

func (*Buffer) Write ¶

Write appends the contents of p to the buffer, growing the buffer as needed. The return value n is the length of p; err is always nil. If the buffer becomes too large, Write will panic with ErrTooLarge .

func (*Buffer) WriteByte ¶

WriteByte appends the byte c to the buffer, growing the buffer as needed. The returned error is always nil, but is included to match bufio.Writer 's WriteByte. If the buffer becomes too large, WriteByte will panic with ErrTooLarge .

func (*Buffer) WriteRune ¶

WriteRune appends the UTF-8 encoding of Unicode code point r to the buffer, returning its length and an error, which is always nil but is included to match bufio.Writer 's WriteRune. The buffer is grown as needed; if it becomes too large, WriteRune will panic with ErrTooLarge .

func (*Buffer) WriteString ¶

WriteString appends the contents of s to the buffer, growing the buffer as needed. The return value n is the length of s; err is always nil. If the buffer becomes too large, WriteString will panic with ErrTooLarge .

func (*Buffer) WriteTo ¶

WriteTo writes data to w until the buffer is drained or an error occurs. The return value n is the number of bytes written; it always fits into an int, but it is int64 to match the io.WriterTo interface. Any error encountered during the write is also returned.

type Reader ¶

A Reader implements the io.Reader , io.ReaderAt , io.WriterTo , io.Seeker , io.ByteScanner , and io.RuneScanner interfaces by reading from a byte slice. Unlike a Buffer , a Reader is read-only and supports seeking. The zero value for Reader operates like a Reader of an empty slice.

func NewReader ¶

NewReader returns a new Reader reading from b.

func (*Reader) Len ¶

Len returns the number of bytes of the unread portion of the slice.

func (*Reader) Read ¶

Read implements the io.Reader interface.

func (*Reader) ReadAt ¶

ReadAt implements the io.ReaderAt interface.

func (*Reader) ReadByte ¶

ReadByte implements the io.ByteReader interface.

func (*Reader) ReadRune ¶

ReadRune implements the io.RuneReader interface.

func (*Reader) Reset ¶ added in go1.7

Reset resets the Reader to be reading from b.

func (*Reader) Seek ¶

Seek implements the io.Seeker interface.

func (*Reader) Size ¶ added in go1.5

Size returns the original length of the underlying byte slice. Size is the number of bytes available for reading via Reader.ReadAt . The result is unaffected by any method calls except Reader.Reset .

func (*Reader) UnreadByte ¶

UnreadByte complements Reader.ReadByte in implementing the io.ByteScanner interface.

func (*Reader) UnreadRune ¶

UnreadRune complements Reader.ReadRune in implementing the io.RuneScanner interface.

func (*Reader) WriteTo ¶ added in go1.1

WriteTo implements the io.WriterTo interface.

Source Files ¶

Keyboard shortcuts.

IncludeHelp_logo

  • Data Structure
  • Coding Problems
  • C Interview Programs
  • C++ Aptitude
  • Java Aptitude
  • C# Aptitude
  • PHP Aptitude
  • Linux Aptitude
  • DBMS Aptitude
  • Networking Aptitude
  • AI Aptitude
  • MIS Executive
  • Web Technologie MCQs
  • CS Subjects MCQs
  • Databases MCQs
  • Programming MCQs
  • Testing Software MCQs
  • Digital Mktg Subjects MCQs
  • Cloud Computing S/W MCQs
  • Engineering Subjects MCQs
  • Commerce MCQs
  • More MCQs...
  • Machine Learning/AI
  • Operating System
  • Computer Network
  • Software Engineering
  • Discrete Mathematics
  • Digital Electronics
  • Data Mining
  • Embedded Systems
  • Cryptography
  • CS Fundamental
  • More Tutorials...
  • Tech Articles
  • Code Examples
  • Programmer's Calculator
  • XML Sitemap Generator
  • Tools & Generators

IncludeHelp

Golang Tutorial

  • Go - Keywords
  • Go - Data Types
  • Go - Variables
  • Go - Constants
  • Go - Program Structure
  • Go - Comments
  • Go - Variable Naming
  • Go - Declaring Multiple Variables
  • Go - Find Variable Type
  • Go - Binary Literals
  • Go - Octal Literals
  • Go - Hexadecimal Literals
  • Go - Type Conversion
  • Go - Operators
  • Go - Arithmetic Operators
  • Go - Relational Operators
  • Go - Logical Operators
  • Go - Bitwise Operators
  • Go - Assignment Operators
  • Go - Miscellaneous Operators
  • Go - if statement
  • Go - if-else statement
  • Go - if-else-if ladder statement
  • Go - nested if statement
  • Go - switch statement
  • Go - select statement
  • Go - Functions
  • Go - Strings

Golang Reference

  • Go - builtin Package
  • Go - bytes Package
  • Go - fmt Package
  • Go - math Package
  • Go - os Package
  • Go - sort Package
  • Go - strconv Package
  • Go - strings Package
  • Go - unicode Package
  • Golang Programs
  • Golang Programs - Home
  • Go - Basic Programs
  • Go - Switch Statement Programs
  • Go - goto Statement Programs
  • Go - Looping Programs
  • Go - Array Programs
  • Go - String Programs
  • Go - Structure Programs
  • Go - User-defined Function Programs
  • Go - Variadic Function Programs
  • Go - Recursion Programs
  • Go - Pointer Programs
  • Go - Conversion Programs
  • Go - Slices Programs
  • Go - Date & Time Programs
  • Go - Timers & Tickers Programs
  • Go - Goroutines Programs
  • Go - Reflection Programs
  • Go - Maps Programs
  • Go - Range Programs
  • Go - Channels Programs
  • Go - File Handling Programs
  • Go - Buffered Input-Output Programs
  • Go - Command-line Arguments Programs
  • Go - Regular Expressions Programs
  • Go - JSON Programs
  • Go - os Package Programs
  • Go - strconv Package Programs
  • Go - log Package Programs
  • Go - math/rand Package Programs
  • Go - math/bits Package Programs
  • Go - sync/atomic Package Programs
  • Go - path/filepath Package Programs
  • Go - syscall Package Programs
  • Go - Signals Programs
  • Go - Shell Script Programs

Golang Practice

  • Golang Interview Questions
  • Golang Find Output Programs
  • Golang Aptitude Questions

Golang Miscellaneous

  • Go - Print Boolean Value
  • Go - Print Double-quoted String
  • Go - Convert From Int to Binary
  • Go - Convert From Int to Octal
  • Go - Convert From Int to Hex
  • Go - Check if Structure is Empty
  • Go - Check if Key Exists in Map
  • Go - Return an Error
  • Go - new() & make() Functions

Home » Golang » Golang FAQ

How to assign string to bytes array in Golang?

Go language | Learn, how to assign a string to the bytes array and how to append a string at the end of the bytes array? Submitted by IncludeHelp , on October 19, 2021

The simple, easy, and safest way is to assign a string to bytes array is,

Consider the below example,

Golang FAQ »

Comments and Discussions!

Load comments ↻

  • Marketing MCQs
  • Blockchain MCQs
  • Artificial Intelligence MCQs
  • Data Analytics & Visualization MCQs
  • Python MCQs
  • C++ Programs
  • Python Programs
  • Java Programs
  • D.S. Programs
  • C# Programs
  • JavaScript Examples
  • jQuery Examples
  • CSS Examples
  • C++ Tutorial
  • Python Tutorial
  • ML/AI Tutorial
  • MIS Tutorial
  • Software Engineering Tutorial
  • Scala Tutorial
  • Privacy policy
  • Certificates
  • Content Writers of the Month

Copyright © 2024 www.includehelp.com. All rights reserved.

GO Bytes to String Conversion Best Practices [5 Methods]

May 8, 2024

Byte slices ( []byte ) and strings are two of the basic data structures in Go programming language used to represent both text and binary data. These can be sequences of characters, but differ in a number of aspects such as mutability, usage and internal representation. In Go, a string is an immutable sequence of bytes with a definite length which makes it safe to share these objects among multiple goroutines concurrently. Conversely, a slice of bytes is capable of being modified and refers to an array containing those bytes. Because of this, an operation to convert or bridge between the two types has become commonplace for input-output tasks, file handling or network communications.

Different Methods for GO Bytes to String conversion

  • Direct conversion using type casting.
  • Use the fmt.Sprintf function for string formatting.
  • Utilizing the strings.Builder for efficient concatenation in loops.
  • Applying the strconv package for conversion with numeric interpretation.
  • The bytes.Buffer helps you convert and manipulate byte slices.
  • Reads bytes from files into strings straight from io/ioutil packages (note that ioutil has been deprecated since Go 1.16, and similar functionality is now available in os and io ).
  • For go versions before 1.16, use io/ioutil package’s ReadAll function combined with a reader interface then cast.
  • Using os package functions read into a byte slice from files or other sources of input before converting

1. Direct conversion using type casting

One of the most basic and simple methods in which you can convert a byte slice ( []byte ) to a string in Go is direct conversion using type casting. What this method does is use Go's type conversion syntax to transfer data from one type to another, which will be on our end []byte to string. If you aren't aware, strings in Go are a read-only slice of bytes. That being known, there's no need for complex processes or external library functions when converting from []byte aka the byte slice that we're given. All we need to do is instruct the compiler directly by treating it as a string. This operation isn't only very efficient because it doesn't copy any bytes but instead creates a header that points to the same underlying byte array.

Examples of Direct Type Conversion :

Basic Conversion :

Here, we've got a straightforward example where a byte slice b representing the word "Hello" is converted directly to a string s .

Numeric Byte Values :

This example uses the ASCII values for the characters. When converted, it still produces the word "Hello" as a string.

Non-ASCII Characters :

Go's string and byte slice types support UTF-8 encoding natively. Here, the byte slice represents the Chinese phrase "你好" (Hello).

Empty Byte Slice :

Even when dealing with empty byte slices, direct conversion results in an empty string without any issues.

2. Using fmt.Sprintf

The Go standard library has a versatile format string function , fmt.Sprintf . It not only converts different data types into string but also formats them in accordance with specific patterns. In terms of converting byte slices ( []byte ) to strings, fmt.Sprintf offers an option for doing so along with additional formatting options if required. On the other hand, while fmt.Sprintf is not the most direct way to convert bytes to a string because it incurs the overhead of handling a format string, it proves useful when you want to either include that byte slice within a larger string or get more out of the byte data in question by giving it particular forms of formatting.

If you want to convert a byte slice into a string using fmt.Sprintf then you can apply %s format verb which implies that argument should be formatted as a string. Here are some examples of Converting Byte Slices to Strings using fmt.Sprintf :

In this example, fmt.Sprintf is employed to convert the byte slice b to a string. The format verb %s is used to represent the byte slice as a string.

With Additional Formatting :

Here, we not only perform the go bytes to string conversion but also embed the resulting string within a larger formatted string.

Conversion Alongside Other Data Types :

This example showcases the power of fmt.Sprintf to handle multiple data types concurrently , integrating the byte-to-string conversion seamlessly within a broader context.

Hexadecimal Representation :

Using the %x verb, we can represent the byte slice in its hexadecimal form, illustrating fmt.Sprintf 's flexibility beyond mere string representation.

3. Using bytes.Buffer Type

The bytes.Buffer type within the Go library is a variable-sized, very perplexing buffer of bytes with read and write methods. This type is part of the bytes package , and it is commonly used for reading from and writing to buffers. The bytes.Buffer in Go is used for efficient manipulation and conversion of byte slices.

Examples of Conversion Using the bytes.Buffer Type :

Here, we initiate a Buffer , write our byte slice b to it, and then effortlessly convert it into a string using the String() method.

Concatenating Multiple Byte Slices :

With Buffer , concatenating multiple byte slices becomes a breeze. This approach is not only intuitive but also efficient, especially when dealing with multiple byte slices.

Applying Transformations Before Conversion :

One of the beauties of Buffer is its seamless integration with other packages, allowing for transformations, like converting to lowercase, post-conversion.

Efficient Handling of Large Byte Slices :

Buffer type is a memory efficient way to convert a bunch of bytes into a string, it just works by minimizing allocations during operations.

Honestly direct conversion and fmt.Sprintf can do the job for basic use cases. But there are scenarios where the Buffer approach can be advantageous:

  • Large Data Handling : If you're working with vast amounts of data, the Buffer type will make sure your memory usage stays efficient
  • Multiple Concatenations:  Let's say you need to add or combine multiple byte slices, Buffer performs significantly better than simple slice appending.
  • Flexibility : It plays well with other Go packages like strings, allowing you to execute many different operations beyond just converting things.

4. Using unsafe

The unsafe package is a unique one within the vast realm of packages that Go offers. While other packages focus on more general, mundane tasks, unsafe says “screw that” and shows you Go’s memory representation and its type system. But if its name is asking for caution, it’s probably for good reason. Of the many things you can do with this package, one that stands out is how it handles byte to string conversion. But as versatile as it may be, its risks are equally noteworthy.

General Summary: The Go package called unsafe is a double-edged sword. Although it gives developers direct access to manipulating memory and converting types at a low level, it ditches the safety mechanism in place by default in Go's type system. This means applications built using this package might run into unexpected behaviors or vulnerabilities if misused.

Examples of Conversion Using the unsafe Package :

Basic Unsafe Conversion :

Here, we directly manipulate memory pointers to cast a byte slice to a string. It's quick and allocates no additional memory, but it's, well, unsafe.

Pitfalls of Using Unsafe : Suppose you modify the original byte slice after converting it using unsafe :

In this example, we saw just how closely tied the byte slice and string are when converted with unsafe . Changing one will affect the other — something you wouldn’t expect at first glance and could pose dangers in some situations.

Possible Pitfalls : You don’t need me to tell you that bypassing type safety opens up a world of opportunities for optimizations. However, these opportunities come with risks:

  • Memory Safety : Go’s robust type system goes a long way in preventing common programming mistakes. Going around it leaves open entirely new ones related to memory management.
  • Maintenance Trouble : Code that uses this package can become harder to maintain and evolve as changes are made to the language or compiler itself.
  • Data Integrity : If there’s anything these examples have shown us, it’s that data integrity can be compromised when sharing references between unsafe variables that stakeholders may modify incorrectly.

5. Using strings.Builder Method

The strings.Builder type within Go's standard library was designed for efficiently building strings through data appending, including but not limited to: bytes or strings themselves. Strings.Builder provides a way for developers like yourself to construct strings more efficiently than with concatenation. In scenarios where multiple appends are performed, it minimizes memory copying and allocation which makes it faster than other methods.

Although strings.Builder has been crafted primarily for string construction rather than converting byte slices; That doesn’t mean you can’t use it for that purpose — especially if many appends will be performed before doing so

Examples of Conversion Using the strings.Builder Method :

Basic Conversion using strings.Builder :

Here, we initiate a strings.Builder , write our byte slice to it, and then convert it to a string.

Appending Multiple Byte Slices :

Using strings.Builder makes adding multiple byte slices together before completing the string switch so much easier.

Comparing strings.Builder with bytes.Buffer : Strings.Builder and bytes.Buffer may seem to do similar things but they have their distinctions:

  • Purpose Specificity :  bytes.Buffer is considered a more general-purpose buffer because it holds both bytes and strings. On the other hand, string builders only work with string building, this means that code using them is much clearer in its intent.
  • Performance : For tasks that only deal with building strings, strings.Builder might be faster due to it being stripped of additional functionality provided by bytes.Buffer .
  • Methods Availability:   bytes.Buffer has a bigger selection of methods for more flexible byte-based operations while string builders have minimalistic APIs.

In the course of learning how to convert byte slices to strings in Go, we have seen that several methods can be used with their own unique characteristics and appropriate applications. As earlier stated, a variety of tools are out there for Go programmers:

  • Direct Type Conversion : A simple and effective technique which is suitable for almost all common situations.
  • Using fmt.Sprintf : This approach is flexible giving more than just conversion and especially if formatting is important.
  • The bytes.Buffer Type : It’s perfect when manipulating or concatenating byte slices needs to be done efficiently.
  • The Risky unsafe Package : The best specialist tool ever known which one should not touch unless there is a compelling performance need, even then with great care.
  • The strings.Builder Method : This method is specialized and optimized for string building hence it finds use in applications where string manipulation dominates.

All in all, the “go bytes to string” problem reflects the richness of Go as a language. However, every single method has its peculiarities such that it might be judged pragmatically given certain aspects including speed, safety and readability.

Deepak Prasad

Deepak Prasad

He is the founder of GoLinuxCloud and brings over a decade of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels in various domains, from development to DevOps, Networking, and Security, ensuring robust and efficient solutions for diverse projects. You can connect with him on his LinkedIn profile.

Can't find what you're searching for? Let us assist you.

Enter your query below, and we'll provide instant results tailored to your needs.

If my articles on GoLinuxCloud has helped you, kindly consider buying me a coffee as a token of appreciation.

Buy GoLinuxCloud a Coffee

For any other feedbacks or questions you can send mail to [email protected]

Thank You for your support!!

Leave a Comment Cancel reply

Save my name and email in this browser for the next time I comment.

Notify me via e-mail if anyone answers my comment.

golang byte assignment

We try to offer easy-to-follow guides and tips on various topics such as Linux, Cloud Computing, Programming Languages, Ethical Hacking and much more.

Recent Comments

Popular posts, 7 tools to detect memory leaks with examples, 100+ linux commands cheat sheet & examples, tutorial: beginners guide on linux memory management, top 15 tools to monitor disk io performance with examples, overview on different disk types and disk interface types, 6 ssh authentication methods to secure connection (sshd_config), how to check security updates list & perform linux patch management rhel 6/7/8, 8 ways to prevent brute force ssh attacks in linux (centos/rhel 7).

Privacy Policy

HTML Sitemap

string and []byte conversion problem in golang

Table of contents.

Let’s look at the problematic code, similar string and []byte conversion code is very common on the web.

The reason why people don’t want to convert a string to a []byte directly via []byte(string) is because that would involve a memory copy, whereas a type conversion via unsafe.Pointer does not involve a memory copy, thus improving performance.

Is there a problem with this code or not? Actually, when I copied the code into vscode, I was prompted with

SliceHeader is the runtime representation of a slice. It cannot be used safely or portably and its representation may change in a later release. Moreover, the Data field is not sufficient to guarantee the data it references will not be garbage collected, so programs must keep a separate, correctly typed pointer to the underlying data.

Firstly, reflect.SliceHeader is used as a runtime representation of a slice, which may change later, and there is a risk of using it directly; secondly, there is no guarantee that the data it points to will not be rubbish collected by GC.

The first problem is fine, but the latter, the GC problem, is a big one! Why there is a GC problem, let’s look at the definitions of reflect.SliceHeader and reflect.

As you can see above, Data is of type uintptr, and although it has a ptr suffix, it is still essentially an integer, not a pointer, which means that it does not hold the data it points to, so the data may be reclaimed by GC.

Now that we know the cause and effect, let’s construct a piece of code that demonstrates that there is a GC problem.

Note: Dynamic strings are used because static strings are stored in the TEXT area and will not be reclaimed by the GC.

When we run the above code, it does not output hello, but gibberish, because the corresponding data has already been reclaimed by the GC, if we remove runtime.GC() and run it again, the output will probably be normal again.

This shows that because Data is of type uintptr, any assignment to it is unsafe. This should have been the end of the matter, but the unsafe.Pointer documentation happens to have an example of a direct assignment to Data: Conversion of a reflect.SliceHeader or reflect.Pointer.

Whether the documentation is wrong or our inference is wrong, continue to read the documentation which states.

the reflect data structures SliceHeader and StringHeader declare the field Data as a uintptr to keep callers from changing the result to an arbitrary type without first importing “unsafe”. However, this means that SliceHeader and StringHeader are only valid when interpreting the content of an actual slice or string value.

That is, the SliceHeader or StringHeader is only valid if it operates on a slice or string that actually exists, and recall that the original code did not comply with unsafe.Pointer because there was no actual slice when it operated on reflect. Pointer ( golang-nuts ), adjust it as required.

Running it through the test code again, the output is now normal. However, some people may ask, didn’t we say before that uintptr is not a pointer and can’t prevent data from being reclaimed by GC, but why does GC have no effect? In fact, this is because the compiler did special handling on the *reflect.{Slice,String}Header.

If you want to verify that there is special handling, you can use a custom type reversal to verify that.

You’ll notice that if you don’t use the type in reflect, then the output doesn’t work again. This backfires and verifies that the compiler is indeed doing something special with *reflect.{Slice,String}Header .

Now that we’ve basically figured out the pitfalls of string and []byte conversions, here’s a look at how to write accurate conversion code, although the compiler plays a few tricks with it, but we shouldn’t rely on these underhanded operations.

Since uintptr is not a pointer, let’s use unsafe.Pointer instead, so that the data is not reclaimed by GC.

The above code is slightly bloated, for a simpler way of writing it see gin or fasthttp for implementations.

At this point, we have solved the problem of converting string and []byte perfectly.

  • Memory Layouts

This articles will introduce type alignment and size guarantees in Go. It is essential to know the guarantees to estimate the sizes of struct types and properly use the 64-bit functions in sync/atomic standard package.

Go is a C family language, so many concepts talked in this article are shared with C language.

Type Alignment Guarantees in Go

To fully utilize CPU directives and get the best performance, the (starting) addresses of the memory blocks allocated for values of a specified type must be aligned as multiples of an integer N, then N is called the value address alignment guarantee of the type, or simply the alignment guarantee of the type. We can also say the addresses of addressable values of the type are guaranteed to be N-byte aligned.

In fact, each type has two alignment guarantees, one is for when it is used as field types of other (struct) types, the other is for other cases (when it is used for a variable declaration, array element type, etc). We call the former one the field alignment guarantee of that type, and call the latter one the general alignment guarantee of that type.

For a type T , we can call unsafe.Alignof(t) to get its general alignment guarantee, where t is a non-field value of type T , and call unsafe.Alignof(x.t) to get its field alignment guarantee, where x is a struct value and t is a field value of type T .

Calls to the functions in the unsafe standard code packages are always evaluated at compile time.

At run time, for a value t of type T , we can call reflect.TypeOf(t).Align() to get the general alignment guarantee of type T , and call reflect.TypeOf(t).FieldAlign() to get the field alignment guarantee of type T .

For the current standard Go compiler (v1.23.n), the field alignment guarantee and the general alignment guarantee of a type are always equal. For gccgo compiler, the statement is false.

The following minimal alignment properties are guaranteed: 1. For a variable x of any type: unsafe.Alignof(x) is at least 1 . 2. For a variable x of struct type: unsafe.Alignof(x) is the largest of all the values unsafe.Alignof(x.f) for each field f of x , but at least 1 . 3. For a variable x of array type: unsafe.Alignof(x) is the same as the alignment of a variable of the array's element type.

So Go specification doesn't specify the exact alignment guarantees for any types. It just specifies some minimal requirements.

Here, the size of a native word (or machine word) is 4-byte on 32-bit architectures and 8-byte on 64-bit architectures.

This means, for the current version of the standard Go compiler, the alignment guarantees of other types may be either 4 or 8 , depends on different build target architectures. This is also true for gccgo.

Generally, we don't need to care about the value address alignments in Go programming, except that we want to optimizing memory consumption, or write portable programs which using the 64-bit functions from sync/atomic package. Please read the following two sections for details.

Type Sizes and Structure Padding

Go specification doesn't make value size guarantees for other kinds of types. The full list of sizes of different types settled by the standard Go compiler are listed in value copy costs .

The standard Go compiler (and gccgo) will ensure the size of values of a type is a multiple of the alignment guarantee of the type.

To satisfy type alignment guarantee rules mentioned previously, Go compilers may pad some bytes between fields of struct values. This makes the value size of a struct type may be not a simple sum of the sizes of all fields of the type.

  • the alignment guarantee and size of the built-in type int8 are both one byte.
  • the alignment guarantee and size of the built-in type int16 are both two bytes.
  • the size of the built-in type int64 is 8 bytes, the alignment guarantee of type int64 is 4 bytes on 32-bit architectures and 8 bytes on 64-bit architectures.
  • the alignment guarantees of the types T1 and T2 are their respective largest field alignment guarantees, a.k.a., the alignment guarantee of the int64 field. So their alignment guarantees are both 4 bytes on 32-bit architectures and 8 bytes on 64-bit architectures.
  • the sizes of the types T1 and T2 must be multiples of their respective alignment guarantees, a.k.a., 4N bytes on 32-bit architectures and 8N bytes on 64-bit architectures.

Although T1 and T2 have the same field set, their sizes are different.

One interesting fact for the standard Go compiler is that sometimes zero sized fields may affect structure padding. Please read this question in the unofficial Go FAQ for details.

The Alignment Requirement for 64-bit Word Atomic Operations

64-bit words mean values of types whose underlying types are int64 or uint64 .

The article atomic operations mentions a fact that 64-bit atomic operations on a 64-bit word require the address of the 64-bit word must be 8-byte aligned. This is not a problem for the current 64-bit architectures supported by the standard Go compiler, because 64-bit words on these 64-bit architectures are always 8-byte aligned.

However, on 32-bit architectures, the alignment guarantee made by the standard Go compiler for 64-bit words is only 4 bytes. 64-bit atomic operations on a 64-bit word which is not 8-byte aligned will panic at runtime. Worse, on very old CPU architectures, 64-bit atomic functions are not supported.

On x86-32, the 64-bit functions use instructions unavailable before the Pentium MMX. On non-Linux ARM, the 64-bit functions use instructions unavailable before the ARMv6k core. On both ARM and x86-32, it is the caller's responsibility to arrange for 64-bit alignment of 64-bit words accessed atomically. The first word in a variable or in an allocated struct, array, or slice can be relied upon to be 64-bit aligned.

(Note, although the above quoted texts shown in the bug zone of that documentation, what they describe are not bugs.)

  • The very old CPU architectures are not mainstream architectures nowadays. If a program needs to do synchronization for 64-bit words on these architectures, there are other synchronization techniques to rescue.
  • On other not-very-old 32-bit architectures, there are some ways to ensure some 64-bit words are relied upon to be 64-bit aligned.

The ways are described as the first (64-bit) word in a variable or in an allocated struct, array, or slice can be relied upon to be 64-bit aligned . What does the word allocated mean? We can think an allocated value as a declared variable, a value returned by the built-in make function, or the value referenced by a value returned by the built-in new function. If a slice value derives from an allocated array and the first element of the slice is the first element of the array, then the slice value can also be viewed as an allocated value.

The description of which 64-bit words can be relied upon to be 64-bit aligned on 32-bit architectures is some conservative. There are more 64-bit words which can be relied upon to be 8-byte aligned. In fact, if the first element of an array or slice which element type is a 64-bit word type can be relied upon to be 64-bit aligned, then all elements in the array/slice can also be accessed atomically. It is just some subtle and verbose to make a simple clear description to include all the 64-bit words can be relied upon to be 64-bit aligned on 32-bit architectures, so the official documentation just makes a conservative description.

If a 64-bit word field (generally the first one) of a struct type will be accessed atomically in code, we should always use allocated values of the struct type to guarantee the atomically accessed fields always can be relied upon to be 8-byte aligned on 32-bit architectures. When this struct type is used as the type of a field of another struct type, we should arrange the field as the first field of the other struct type, and always use allocated values of the other struct type.

By using this solution, the Counter type can be embedded in other user types freely and safely, even on 32-bit architectures. The drawback of this solution is there are seven bytes being wasted for every value of Counter type and it uses unsafe pointers. sync standard package uses a [3]uint32 value to do this trick instead . This trick assumes that the alignment guarantee of the uint32 type is a multiple of 4 bytes. The assumption is true for both the standard Go compiler and gccgo compiler. However, it might be false for another third-party Go compiler . -->

https://github.com/golang/go/issues/36606

The Go 101 project is hosted on Github . Welcome to improve Go 101 articles by submitting corrections for all kinds of mistakes, such as typos, grammar errors, wording inaccuracies, description flaws, code bugs and broken links.

If you would like to learn some Go details and facts every serveral days, please follow Go 101's official Twitter account @zigo_101 .

  • Leanpub store , $19.99+ (You can get this book from this boundle which also contains 3 other books, with the same price) .
  • Amazon Kindle store, (unavailable currently) .
  • Apple Books store , $19.99 .
  • Google Play store , $19.99 .
  • Free ebooks , including pdf, epub and azw3 formats.
  • Color Infection (★★★★★), a physics based original casual puzzle game. 140+ levels.
  • Rectangle Pushers (★★★★★), an original casual puzzle game. Two modes, 104+ levels.
  • Let's Play With Particles , a casual action original game. Three mini games are included.
  • About Go 101 - why this book is written.
  • Acknowledgments
  • An Introduction of Go - why Go is worth learning.
  • The Go Toolchain - how to compile and run Go programs.
  • Introduction of Source Code Elements
  • Keywords and Identifiers
  • Basic Types and Their Value Literals
  • Constants and Variables - also introduces untyped values and type deductions.
  • Common Operators - also introduces more type deduction rules.
  • Function Declarations and Calls
  • Code Packages and Package Imports
  • Expressions, Statements and Simple Statements
  • Basic Control Flows
  • Goroutines, Deferred Function Calls and Panic/Recover
  • Go Type System Overview - a must read to master Go programming.
  • Value Parts - to gain a deeper understanding into Go values.
  • Arrays, Slices and Maps - first-class citizen container types.
  • Functions - function types and values, including variadic functions.
  • Channels - the Go way to do concurrency synchronizations.
  • Interfaces - value boxes used to do reflection and polymorphism.
  • Type Embedding - type extension in the Go way.
  • Type-Unsafe Pointers
  • Generics - use and read composite types
  • Reflections - the reflect standard package.
  • Line Break Rules
  • More About Deferred Function Calls
  • Some Panic/Recover Use Cases
  • Explain Panic/Recover Mechanism in Detail - also explains exiting phases of function calls.
  • Code Blocks and Identifier Scopes
  • Expression Evaluation Orders
  • Value Copy Costs in Go
  • Bounds Check Elimination
  • Concurrency Synchronization Overview
  • Channel Use Cases
  • How to Gracefully Close Channels
  • Other Concurrency Synchronization Techniques - the sync standard package.
  • Atomic Operations - the sync/atomic standard package.
  • Memory Order Guarantees in Go
  • Common Concurrent Programming Mistakes
  • Memory Blocks
  • Memory Leaking Scenarios
  • Some Simple Summaries
  • Value Conversion, Assignment and Comparison Rules
  • Syntax/Semantics Exceptions
  • Go Details 101
  • Go Tips 101
  • More Go Related Topics

Navigation Menu

Search code, repositories, users, issues, pull requests..., provide feedback.

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly.

To see all available qualifiers, see our documentation .

  • Notifications You must be signed in to change notification settings

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement . We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmd/compile: memory corruption when setting outer values in functional iterators #70035

@scr-oath

scr-oath commented Oct 25, 2024

@gabyhelp

gabyhelp commented Oct 25, 2024

Sorry, something went wrong.

@prattmic

prattmic commented Oct 25, 2024

No branches or pull requests

@prattmic

ESP32 UART Communication (Serial): Set Pins, Interfaces, Send and Receive Data (Arduino IDE)

This is a simple guide about UART serial communication protocol with the ESP32 using Arduino IDE. We’ll take a look at the basics of UART, default and custom UART pins, basic functions, and communication between boards.

ESP32 UART Communication Serial Set Pins,  Interfaces, Send and Receive Data

Table of Contents:

Throughout this guide, we’ll cover the following topics:

UART Interface

Esp32 uart peripherals, esp32 default uart pins, uart0 and the serial monitor, esp32 setting custom uart pins, serial communication with a gps module, esp32 serial communication between boards (sender and receiver), prerequisites.

This tutorial focuses on programming the ESP32 using the Arduino core. Before proceeding, you should have the ESP32 Arduino core installed in your Arduino IDE. Follow the next tutorial to install the ESP32 on the Arduino IDE, if you haven’t already.

  • Installing the ESP32 Board in Arduino IDE (Windows, Mac OS X, and Linux instructions)

Alternatively, you can also use VS Code with the PlatformIO extension to program your boards using the Arduino core:

  • Getting Started with VS Code and PlatformIO IDE for ESP32 and ESP8266 (Windows, Mac OS X, Linux Ubuntu)

Introducing ESP32 UART Communication Protocol

UART (Universal Asynchronous Receiver-Transmitter) is a serial communication protocol that allows two devices to communicate.

Unlike SPI or I2C, which are synchronous, UART is asynchronous, meaning it does not use a clock signal to synchronize the data transmission between devices. However, both devices must agree on the baud rate (speed of transmission).

UART communication with ESP32 explained

In UART communication, data is transferred serially, bit by bit (hence the term serial ), at a pre-defined baud rate (bits per second). UART uses a single data line for transmitting (TX) and one for receiving (RX).

UART ports allow us to communicate with other devices, such as other microcontroller boards (an Arduino, an ESP8266, another ESP32 board, or others), the computer, sensors, GPS or Bluetooth modules , some types of displays, and more.

For UART communication, you need the following lines:

  • TX (Transmit) : Sends data.
  • RX (Receive) : Receives data.
  • GND : Common ground

The ESP32 supports up to three UART interfaces: UART0 , UART1 , and UART2 , depending on the ESP32 board model you’re using.

  • UART0 is usually reserved for communication with the serial monitor during upload and debugging. However, you can also use it for communication with other devices after uploading the code if the Serial Monitor is not needed.
  • UART1 and UART2 : available to communicate with external devices.

Like I2C and SPI, these UART pins can be mapped to any GPIO pin on the ESP32. However, they have a default pin assignment on most board models.

For most ESP32 boards the UART pin assignment is as follows:

About UART1 (GPIO 9 and GPIO10) – these GPIOs are connected to the ESP32 SPI flash memory, so you can’t use them like that. To use UART1 to communicate with other devices, you must define different pins using the HardwareSerial library. 

If you’re using an ESP32-S3 , the assignment is completely different. The following table shows the default UART0, UART1, and UART2 RX and TX pins for the ESP32-S3 :

Note : Depending on the board, the default UART pins might be different. Always check the pinout for your specific board. You can also reassign UART pins in code if your board doesn’t have them pre-assigned.

In most IDEs like the Arduino IDE or PlatformIO, the Serial Monitor interacts with the ESP32 over a USB connection. This is internally mapped to UART0 on the ESP32, so when you open the Serial Monitor, you are using UART communication to send and receive messages.

Note: you can use UART0 to communicate with other devices after uploading code to the board if you don’t use the Serial Monitor.

Here’s a basic example that shows a two-way communication between the ESP32 and the Serial Monitor (your computer).

View raw code

When you use Serial.begin(115200) you are initializing a serial communication using UART0 at a 115200 baud rate.

To write data to the Serial Monitor, you use the print() or println() methods on the Serial instance.

You can also send data from the Serial Monitor to the ESP32. To read that data, you can use the read() method. To check if there are any available bytes to read, you can use the available() method.

Testing the Example

Upload the code to your ESP32. After uploading, open the Serial Monitor at a baud rate of 115200. Then, press the ESP32 RST button to start running the program.

Uploading code to ESP32 Arduino IDE

It should print a message “ ESP32 is ready. Please enter a message. “

There’s a field on the Serial Monitor where you can write data to send to the ESP32 (highlighted in yellow). Write something on that field and press enter to send it to the ESP32. It will be printed back.

Testing the Serial Monitor UART communication

Setting custom UART pins is quite simple. As we’ve seen previously, the ESP32 has three UART ports you can use: UART0, UART 1 and UART 2.

To use any of those ports, you just need to create an HardwareSerial instance on the desired UART port. For example, the following line creates a serial instance called mySerial using UART2.

Then, you can use any pins of your choice. You just need to pass them to the begin() method like this:

In which RX_GPIO and TX_GPIO are the UART pins of your choice.

UART Communication with Other Devices

Now, let’s take a look at how to use UART to communicate with other devices. We’ll see an example to communicate with a GPS module, and an example to communicate with another ESP32.

To start a UART communication in your ESP32 code, you need to specify the UART port, baud rate, and pin mapping. Here’s an example of initializing UART communication with a GPS module—this can be applied to other modules or devices.

This sketch assumes you are using GPIO 16 and GPIO 17 as RX and TX serial pins to establish serial communication with the GPS module. If you’re using other pins you should edit that on the following lines:

Then, we define the module baud rate on the following line.

We create an instance of the HardwareSerial to use UART 2 called gpsSerial .

In the setup() , we initiate the Serial Monitor.

Next, we initialize a serial communication with the GPS module using the begin() method on the gpsSerial() instance.

The begin() method accepts as arguments the baud rate, communication frame format (data, parity and stop bits, SERIAL_8N1 is the default), and RX and TX pins.

SERIAL_8N1 means 8 data bits, No parity, 1 stop bit, which is the most commonly used. You can check all the options here .

In the loop() , the code checks if there is GPS data on the serial port with the available() method.

When data is available, we read it using the read() method and then print it in the Serial Monitor.

For the complete instructions to test this code, make sure to take a look at our GPS Module tutorial: ESP32 with NEO-6M GPS Module (Arduino IDE) .

If you want to communicate with another ESP32, it is not much different. You basically initialize a serial communication on the desired GPIOs and then use the functions to read and send data via serial.

To show you how this works, we’ll send data via Serial from one ESP32 board to the other. The ESP32 sender will continuously send a message with a counter over UART. The ESP32 receiver board receiver will receive the number from the other board.

To test the example:

  • you need two ESP32 boards;
  • one board will be the sender and the other will be the receiver
  • we’ll use UART1 and we’ll assign TX to GPIO 19 and RX to GPIO 21 (you can use any other pins)—we’re not using the default pins to show you how it’s done;
  • you need to connect the RX from one board to the TX of the other and connect the GNDs together

ESP32 UART communication between boards

ESP32 Sender Code

Here’s the code for the ESP32 sender. Upload it to your board.

How Does the Code Work?

Let’s take a look at how the code works.

Start by defining the TX and RX pins you want to use. We’re using GPIO19 and GPIO21, but you can use any other pins.

We’re using UART1, but for this example we could also have chosen UART2. To use UART1, we start by creating an HardwareSerial instance called mySerial .

Then, we create a variable to hold the number we want to send to the other board called counter .

In the setup() , we initialize the serial communication on UART1 by calling the begin() method on the mySerial object.

In the loop() , we send the counter via serial by using the println() method on our mySerial object. This is what will send the number to the other board.

Then, we increment the counter on each loop.

A new message is sent every second, but you can change the delay time if needed.

Uploading the Code

Upload the code to the sender board. After uploading open the serial monitor at a baud rate of 115200. You’ll see that it will start sending data via UART.

ESP32 UART Sender Demonstration

Now, let’s prepare the receiver to receive the data.

ESP32 Receiver Code

Here’s the code for the ESP32 receiver. Upload it to your board.

Setting up the serial connection is the same as we’ve seen in the previous example. The loop() is different. In this case, we’ll listen for incoming data.

Then, to receive data from the other board, we start by checking if there are any available bytes to read.

Then, we read the incoming data and saved it in the message variable using the readStringUntil() method.

This method reads data from the UART serial buffer as a String, continuing to read characters until it encounters the newline character ‘ \n ‘ .

The ‘ \n ‘ is typically used to mark the end of a message or line. When it encounters this character in the incoming data stream, it stops reading and returns the string up to that point.

Upload the Code

Upload the previous code to the receiver board. Open a Serial Monitor connection with this new board.

ESP32 communication via UART

The ESP32 sender will continuously send a message with a counter over UART. The ESP32 board receiver will receive the number from the other board.

ESP32 UART receiver demonstration

Now you have one ESP32 board sending data to the other.

Wrapping Up

This tutorial was a getting started guide to UART communication with the ESP32. We’ve seen how UART works, how to use the ESP32 UART ports on the desired pins, and how to send data from one ESP32 to the other.

We hope you found this guide useful.

We have guides for other communication protocols with the ESP32 that you may find useful.

  • ESP32 I2C Communication: Set Pins, Multiple Bus Interfaces and Peripherals (Arduino IDE)
  • ESP32 SPI Communication: Set Pins, Multiple SPI Bus Interfaces, and Peripherals (Arduino IDE)
  • ESP32 Wireless Communication Protocols

Learn more about the ESP32 with our resources:

  • Learn ESP32 with Arduino IDE (eBook)
  • Free ESP32 Projects and Tutorials

Thanks for reading.

golang byte assignment

SMART HOME with Raspberry Pi, ESP32, ESP8266 [eBook]

Recommended resources.

golang byte assignment

Build a Home Automation System from Scratch »   With Raspberry Pi, ESP8266, Arduino, and Node-RED.

golang byte assignment

Home Automation using ESP8266 eBook and video course »   Build IoT and home automation projects.

golang byte assignment

Arduino Step-by-Step Projects » Build 25 Arduino projects with our course, even with no prior experience!

What to Read Next…

Programming the ESP32 CYD Cheap Yellow Display with VS Code and PlatformIO

Programming the ESP32 CYD (Cheap Yellow Display) with VS Code

ESP32 ESP8266 Input Data on HTML Form Web Server using Arduino IDE

Input Data on HTML Form ESP32/ESP8266 Web Server using Arduino IDE

golang byte assignment

ESP8266 Multisensor Shield with Node-RED

Enjoyed this project stay updated by subscribing our newsletter, 3 thoughts on “esp32 uart communication (serial): set pins, interfaces, send and receive data (arduino ide)”.

Thanks a lot: this is very useful to many of us. Anyway compiling sketches for the ESP32 using Arduino IDE is almost unpossible because a very long time. Normally it takes about 3 or more minutes to compile and this is not acceptable for a practical use. Any solution to this big problem ? Thanks

It may be due to updates on the Arduino IDE. I’m not sure… Regards, Sara

I know lot of people that are expeiencing a very long compiling time as I told. Question: are you experiencing the same problem , please ? If Not, could you please inform about your configuration ? Thanks

Leave a Comment Cancel reply

Notify me of follow-up comments by email.

Notify me of new posts by email.

Download Our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.

IMAGES

  1. Byte Buffer with Golang. Byte Buffer with Golang

    golang byte assignment

  2. GoLang Tutorial

    golang byte assignment

  3. GoLang Tutorial

    golang byte assignment

  4. Golang 中的 bytes 包详解(一):bytes.Buffer

    golang byte assignment

  5. GoLang Tutorial

    golang byte assignment

  6. Strings, Bytes and Runes

    golang byte assignment

VIDEO

  1. Map-Reduce Golang Assignment Test Cases Passed

  2. golang 12

  3. Golang backend assignment Demo

  4. golang 5

  5. Solving A Subscriber's Golang Interview Assignment!

  6. Chapter 3: Assignment Operators

COMMENTS

  1. go

    The built-in copy function only copies to a slice, from a slice.; Arrays are "the underlying data", while slices are "a viewport into underlying data". Using [:] makes an array qualify as a slice.; A string does not qualify as a slice that can be copied to, but it qualifies as a slice that can be copied from (strings are immutable).; If the string is too long, copy will only copy the part of ...

  2. Go byte

    A byte in Go is an unsigned 8-bit integer. It has type uint8. A byte has a limit of 0 - 255 in numerical range. It can represent an ASCII character. Go uses rune, which has type int32, to deal with multibyte characters. The bytes package implements functions for the manipulation of byte slices. It is similar to the strings package. $ go version.

  3. Golang bytes

    The Buffer type comes with the bytes package. Here is the syntax for declaring an empty buffer. var b bytes.Buffer. To write into a Buffer we can use the write function like this. b.Write ([]byte ("a string")) fmt.Println (b.String ()) // a string. The Fprintf can also be used to write into the buffer.

  4. go

    How do you append a byte to a string in Go? var ret string var b byte ret += b invalid operation: ret += b (mismatched types string and byte)

  5. Go Walkthrough: bytes + strings

    In the previous post we covered byte streams but sometimes we need to work with bounded, in-memory byte slices instead. While working with a list of bytes seems simple enough, there are a lot of edge cases and common operations that make using the bytes package worthwhile. We're also going to lump in the strings package in this post since its API is nearly identical although for use with ...

  6. Go Slices: usage and internals

    The slice type is an abstraction built on top of Go's array type, and so to understand slices we must first understand arrays. An array type definition specifies a length and an element type. For example, the type [4]int represents an array of four integers. An array's size is fixed; its length is part of its type ([4]int and [5]int are ...

  7. bytes package

    func (b * Buffer) Bytes() [] byte. Bytes returns a slice of length b.Len () holding the unread portion of the buffer. The slice is valid for use only until the next buffer modification (that is, only until the next call to a method like Buffer.Read, Buffer.Write, Buffer.Reset, or Buffer.Truncate).

  8. How to assign string to bytes array in Golang?

    The simple, easy, and safest way is to assign a string to bytes array is, []byte ("String_Value") Consider the below example, // Go language program to assign string// to bytes arraypackage main import ( "fmt" ) // Main functionfunc main () { // Declare a byte arrayvar b [] byte// Assigning the string to bytes array b = [] byte ("Hello, world ...

  9. Arrays, slices (and strings): The mechanics of 'append'

    Here we see that the contents of a slice argument can be modified by a function, but its header cannot. The length stored in the slice variable is not modified by the call to the function, since the function is passed a copy of the slice header, not the original. Thus if we want to write a function that modifies the header, we must return it as a result parameter, just as we have done here.

  10. GO Bytes to String Conversion Best Practices [5 Methods]

    Different Methods for GO Bytes to String conversion. Direct conversion using type casting. Use the fmt.Sprintf function for string formatting.; Utilizing the strings.Builder for efficient concatenation in loops.; Applying the strconv package for conversion with numeric interpretation.; The bytes.Buffer helps you convert and manipulate byte slices.; Reads bytes from files into strings straight ...

  11. string and []byte conversion problem in golang

    You'll notice that if you don't use the type in reflect, then the output doesn't work again. This backfires and verifies that the compiler is indeed doing something special with *reflect.{Slice,String}Header.. Now that we've basically figured out the pitfalls of string and []byte conversions, here's a look at how to write accurate conversion code, although the compiler plays a few ...

  12. Why i can not assign int to byte

    The constant 22 is not an int - it's an untyped constant. These can take on any numeric type depending on the context. In this case it will become a byte constant since it's used in a byte context. A numeric integer constant will default to the int type if there is no context (var foo = 22 // foo is now int), which is what you're thinking of.

  13. Memory Layouts

    Golang online books, articles, tools, etc. 64-bit words mean values of types whose underlying types are int64 or uint64.. The article atomic operations mentions a fact that 64-bit atomic operations on a 64-bit word require the address of the 64-bit word must be 8-byte aligned. This is not a problem for the current 64-bit architectures supported by the standard Go compiler, because 64-bit words ...

  14. Golang中[]byte与string转换全解析

    string类型和[]byte类型是我们编程时最常使用到的数据结构。本文将探讨两者之间的转换方式,通过分析它们之间的内在联系来拨开迷雾。 两种转换方式. 标准转换; go中string与[]byte的互换,相信每一位gopher都能立刻想到以下的转换方式,我们将之称为标准转换。

  15. cmd/compile: memory corruption when setting outer values in ...

    Bug with []byte struct field at append slice in for range loop #35340 (closed) cmd/compile: ranging exclusively values over slices and arrays that are not cheaply indexable keeps alive 3 registers instead of 2 #61629 (closed) cmd/compile: fix unsafe.{SliceData,StringData} escape analysis memory corruption; affected/package: #57101 (closed)

  16. ESP32 UART

    Like I2C and SPI, these UART pins can be mapped to any GPIO pin on the ESP32. However, they have a default pin assignment on most board models. For most ESP32 boards the UART pin assignment is as follows: ... .println("Serial 2 started at 9600 baud rate"); } void loop(){ while (gpsSerial.available() > 0){ // get the byte data from the GPS char ...

  17. Cannot assign []byte to z (type string) in multiple assignment

    You can convert []byte to string, but you cannot convert one value of a multiple return valued function. buf, err := ioutil.ReadFile(z) if err != nil {. log.Fatal(err) } z = string(buf) However, quite often it's better to not convert binary data to strings and work directly with buf. answered Jul 3, 2013 at 14:04.