🧑🤝🧑 Copy a slice in Go
To duplicate a slice in Go, getting a deep copy of its contents, you need to either use the built-in copy() function, or create a new empty slice and add all the elements of the first slice to it using the append() function. Because of how slices are built in Go , assigning one slice to another only makes a shallow copy, and you should not use it if you want to clone the slice in a deep way.
Copy a slice using the copy() function
As you can see, we got two different addresses of underlying arrays for the src and dst slices, which is evidence that we deeply cloned the src slice. The copy() function copies min(len(dst), len(src)) elements, so we need to create the dst slice of the same size as the src using make([]string, len(src)) .
Copy a slice using the append() function
Copying a slice using the append() function is really simple. You just need to define a new empty slice, and use the append() to add all elements of the src to the dst slice. In that way, you get a new slice with all the elements duplicated.
Shallow copy by assignment
If you just assign the src slice to the new dst variable, you get a shallow copy that has the same underlying array. When you modify the contents of this copy, the original slice will also change.
Thank you for being on our site 😊. If you like our tutorials and examples, please consider supporting us with a cup of coffee and we'll turn it into more great Go examples.
Have a great day!
🔎 Check if the slice contains the given value in Go
Learn how to write a function that checks if a slice has a specific value, 🖨️ convert string to []byte or []byte to string in go, learn the difference between a string and a byte slice, 🧠 print the memory address of a variable in go, learn how to find and print the address of a variable or pointer.
How to Deep Copy or Duplicate a Slice in Go
This article provides a comprehensive guide on duplicating slices in Go, highlighting the importance of using copy() and append() for deep copying.
In Go, simply assigning one slice to another doesn’t create a new, independent copy. Instead, both variables refer to the same underlying array, meaning changes in one slice will reflect in the other.
This shared reference becomes apparent in function calls, as slices are passed by reference:
1. Deep copying with copy()
The built-in copy() function creates an independent copy of a slice. It requires initializing a new slice of the appropriate length first:
The copy() function in Go requires two arguments: the destination slice first and the source slice second, with both being of the same type. It returns the count of elements copied. You can disregard this return value if it’s not needed.
A key aspect of copy() is that the lengths of the destination and source slices can differ; it copies elements only up to the length of the shorter slice. Therefore, copying to a slice that is empty or nil results in no elements being transferred.
An important edge case to note with copy() is that the destination slice remains non-nil, even if the source slice is nil :
To ensure that the destination slice also becomes nil if the source is nil , you can modify your code as follows:
2. Deep copying with append()
The built-in append() offers another way to duplicate a slice, ensuring the new slice is independent of the original:
In this scenario, elements from s1 are appended to a nil slice, and the resultant slice becomes s2 . This approach ensures a complete duplication of the slice, irrespective of the destination’s initial length, unlike the copy() function. It’s important to note that the final length of s2 adjusts to match the length of s1 , either by truncating or expanding as necessary.
When using append() , if the source slice is non-nil but empty, the resulting destination slice becomes nil :
To address this, create a three-index subslice from the original slice and append it instead. This method ensures the new slice does not share any elements with the source by setting both the length and capacity of the subslice to zero:
Final thoughts
In this article, we explored two methods for cloning a slice in Go, while addressing some important edge cases related to either technique. If you have additional insights or methods to share, please feel free to contribute in the comments section.
Thank you for reading, and happy coding!
How to use the copy function
The built-in copy function copies elements into a destination slice dst from a source slice src .
It returns the number of elements copied, which will be the minimum of len(dst) and len(src) . The result does not depend on whether the arguments overlap.
As a special case , it’s legal to copy bytes from a string to a slice of bytes.
Copy from one slice to another
Copy from a slice to itself, copy from a string to a byte slice (special case), further reading.
Slices and arrays in 6 easy steps
Build your own slice: Append and Copy
Did you know that append can turn broccoli into pizza?
While delicious, this is a bit weird.
Appending to the fastfood slice changes elements in the broccoli slice? A slice that is not even mentioned in the call to append ?
This article will explain this madness.
This article is part of a series on slices
- 1. Arrays and slices.
- 2. Append and Copy (this article)
- 3. Creating slices: Make, literals and re-slicing.
- 4. Slices and nil (to be published).
Are you or your team struggling with aspects of Go?
Book a 30 minute check-in with me.
I can help with questions like:
- How should we structure our Go web app?
- Is this package a good choice for us?
- How do I get my team up to speed quickly?
- Can I pick your brain on {subject}?
Looking forward to meeting you.
Recap: Capacity
In the last article we introduced the concept of capacity , the number of elements from the offset to the end of the backing array.
But we didn’t explain why it exists: Managing backing arrays when appending.
Appending to a slice
Appending is sometimes described as “adding elements to a slice”, but that’s not completely accurate.
In Go, appending always creates a new slice. This new slice contains the elements of the original slice plus the elements you wanted to append.
The original slice remains untouched.
Appending is done using the built-in append function, it:
- Accepts a slice and zero or more elements.
- Returns a new slice.
All elements will need to be of the same type as the elements of the slice.
Suppose we have an original slice of strings called fruits that contains ["🍋", "🍎", "🍒"] .
We can then create a new slice food with the extra elements "🍔" , "🌭" and "🍕" :
food will contain ["🍋", "🍎", "🍒", "🍔", "🌭", "🍕"] and fruits remains untouched.
If you don't want to use a second variable, it's important to re-assign the results of the call to append to the original slice.
For example, if we want to append "🍊" to the fruits slice and also assign the results to fruits , we need to run fruits := append(fruits, "🍊") .
fruits would then contain ["🍋", "🍎", "🍒", "🍊"] .
This “append and reassign” is a very common pattern.
Append and arrays
As we saw in the last article , the elements of a slice are not stored in the slice itself, but in an array. One array can be shared by several slices.
A new slice returned by append also need to be backed by an array. This array needs to be large enough to contain both the original elements and the appended elements.
For efficiency, append prefer to not create any new arrays. So if it’s large enough, append will use the array of the original slice to back the new slice.
But, how does append know if the array of the original slice is large enough?
That’s where capacity comes in. By calculating capacity - length , we get the number of available elements in the array of a slice.
If there are enough available elements to fit the appended values, the array of the original slice is large enough. If not, append will create a completely new backing array.
For example, in the above diagram:
- If we append one or two elements, the array of the original slice will be used.
- If we append more than two elements, a new backing array is created.
We will dive a bit deeper into both procedures before we implement them ourselves.
Array of the original slice
If there is enough space in the array of the original slice, append will take two steps:
- Copy each appended value in order into the available elements in the backing array.
- Return a new slice backed by the array of the original slice.
In the following code we append one value to fruits and assign the results to food . We then print both food and a .
I would advise you to try:
- Verify that available elements in a get their values overwritten (even if they’re not empty strings).
- Verify that setting a value in food is reflected in a and fruits .
- Append multiple values. At which point will food receive a new backing array?
If we apply the steps we saw earlier to the example above:
All elements in the original slice remain unchanged: The modified elements in the backing array will always be outside of the “window” of the original slice.
However, this is not necessarily true for other slices that might share that same backing array. It’s possible for a third slice to have a “window” into the modified part of the backing array.
This is what happened with the broccoli and pizza example you saw in the introduction of this article.
Situations like this can be confusing. On the surface it looks like a third slice will have its content changed by a call to append in which it is not mentioned.
When the slicing and call to append happen in different places in your code, it's easy to lose track of what is happening and can lead to tricky bugs.
I generally try to avoid using append with slices that share a backing array.
New backing array
If the original slice’s array does not have enough space append will take the following steps:
- Create a new array that is large enough for the original elements + the appended elements.
- Copy over the original elements.
- Copy over the appended elements after the original elements.
- Return a new slice backed by the new array.
In the following code we append one value to fruits , and we print a to show that the original slice’s backing array has not been modified .
Again, I would advise you to try some things:
- Verify that setting a value in food is not reflected in a and fruits .
- Check the length and capacity of food .
If we again apply the steps to the example:
The new array created by append cannot be accessed directly, but it is possible to get pointers to parts of it.
At the time of writing, the length of this array will be one of the following:
- 2x to 1.25x the capacity of the original slice.
- The exact required length (nr. of original elements + nr. of appended elements).
The exact algorithm will be shown when we implement append ourselves later.
This can be verified by checking the capacity of the new slice using cap .
Since the new slice is backed by a new array, changes to its elements will not be reflected in the original slice.
As you can see, depending on the size of the original slice's array, a call to append can have quite different consequences.
Any time you encounter a situation where a change is (not) reflected in a slice, double check if they share the same backing array. This snippet shows you how to check for shared backing arrays.
Copying a slice
No matter how append constructs the returned slice, it will involve copying elements. Which means that we might need to have some kind of copying function when we implement append ourselves.
Which brings us to the built-in copy function.
copy copies elements between a source and a destination slice and returns the number of copied elements.
Both slices need to have elements of the same type.
Let’s take a look how this works in practice.
In the following example, all elements from src are copied to dst . We print dst and the number of copied elements.
I would advise you to also change the length of src or dst by changing their “windows”. Which elements are copied?
The above example in a diagram:
While playing around with the “windows” you might have discovered: if the two slices are not of equal length, copy only copies the minimum number of elements.
In other words, quoting the Go spec :
The number of elements copied is the minimum of len(src) and len(dst)
If you want to fully copy a source slice you always need to make sure that your destination slice has at least the same length as the source slice.
Build your own
Now that we know how append and copy are supposed to work. Let’s get our hands dirty and implement them ourselves.
We will build on the work we did in the previous article .
Copy function
We will begin with the Copy function, since this is needed for our append implementation.
Based on the built-in copy function, our implementation takes the following shape:
My (and maybe yours as well) first hunch is to implement this using a for loop.
But, if you run the example above, you will see that:
- The fruits are copied correctly.
- Something is going wrong with the veggies : the broccoli is copied multiple times.
What is going wrong?
So "🥦" is copied from veggiesSrc[0] to veggiesDst[0] .
However, since both slices overlap, veggiesDst[0] and veggiesSrc[1] refer to the same element in the backing array. Which means that when we copy from veggiesSrc[1] in the next iteration, we end up copying the "🥦" again.
This repeats until we reach the end of either slice and we end up printing [🥦 🥦 🥦] .
To fix this, we need to keep track of the values in the source slice before we copy any elements.
One way we can do this is using recursion :
copyRecursive stores every source element in memory (the v variable) before calling copyRecursive for the next element. Once the end of either slice is reached, all call will return in turn and set the destination element to v .
If you replace the earlier implementation with the recursive one, you will see that the program now prints [🥦 🥕 🥬] .
Now that we have a working Copy function, we can begin on our Append implementation.
Append function
Again, basing our function signature on the built-in append function, we begin with the following code:
We know that the Append always returns a new slice with a length of original length + number of values .
As we discussed earlier, there are two cases we need to handle:
- Append using the array of the original slice if it has enough capacity.
- Else, append using a new backing array.
Let’s handle the first case first.
There is one thing we need to do: Copy the vals in order into the newS after the elements of the original slice.
In other words, we need to set the values in order after a certain offset. Let’s write a function that handles it for us.
So with what offset do we call this function?
Well, the length of the original slice s .
We can then return newS , since it should use the array , offset and capacity of the original slice.
That’s all there is to the first case. The second case requires a bit more work. We need to:
- Calculate a new capacity.
- Create a new backing array with that capacity.
- Copy the elements of the original slice.
- Copy the vals in order like we do for the first case.
Let’s begin with the capacity.
The Go runtime code to calculate a new capacity can be found here .
If we adapt it to our needs we get the following function:
The new capacity will be double the original capacity, unless:
- The length of the new slice is greater than double the original capacity (we then use the length as capacity).
- The original capacity is greater than or equal to 256 (we then smoothly transition to 1.25x the original capacity).
The Go runtime implementation takes one more step, it rounds the capacity for more efficient memory allocation.
We can now use the calcNewCap function in Append to set the capacity of the new slice.
With the new capacity calculated, we can use reflection to create a new array.
We use reflect.ArrayOf to create a new type. This type is equivalent to [capacity]string .
The new type is used to create a new array value using reflect.New . That value is then assigned to newS.array .
We also reset the newS.offset to 0 . Since we’re starting fresh, we can safely use the entire array.
With the array in place, we then copy the values from the original slice.
We use the Copy function we built earlier to copy the original slice s to the new slice newS .
Since newS.length >= s.length , this will always copy all of the elements of s into newS .
That leaves us with the last step: copying the values after the original elements. Here we can use setValsAfter again.
All steps are in place, this wraps up the Append function. Check the demo below to see the results.
Demo of both the Copy and Append functions.
Phew, that was a bit of work! We discussed:
- Which elements get copied by calling copy .
- What it means to append something to a slice.
- When and how append creates a new backing array.
- The role capacity plays in this.
- Why append can seemingly modify elements in “unrelated” slices.
I hope you learned something :)
The next article in this series will look at the different ways you can create slices in Go.
Keep Learning. Subscribe to my Newsletter.
Gain access to more content and get notified of the latest articles:
- A Brief Guide To Time for Developers
- Source code
I send emails every 1-2 weeks and will keep your data safe . You can unsubscribe at any time.
Hello! I'm the Willem behind willem.dev
I created this website to help new Go developers, I hope it brings you some value! :)
You can follow me on Twitter/X , LinkedIn or Mastodon .
Thanks for reading!
Related Articles
- Build your own slice: Make, Literals and Re-slicing
- Build your own slice: Arrays and slices
- Should you use slices of pointers to structs?
- Should you use pointers to slices in Go?
- Go's time.Time and time.Location explained
Top 5 articles
- URL path parameters in routes
- How to parse a time or date in Go
- Composable HTTP Handlers using generics
- Anonymous structs in Go: What, How and When
- Confused by http.HandlerFunc? This post makes it click
Table of contents
Stuck on go project structure.
Get 25% off on my upcoming guide.
🧠 Keep Learning. Subscribe to my newsletter.
Gain access to exclusive content:
Save 25% when Structuring Go Web Applications by Value is launched.
Stay in the loop. I publish new articles every 1-2 weeks, get a heads-up when one is available.
I will keep your data safe . You can unsubscribe at any time.
I'm the Willem behind willem.dev and I approve of this message.
The Go Blog
Arrays, slices (and strings): the mechanics of 'append'.
Rob Pike 26 September 2013
Introduction
One of the most common features of procedural programming languages is the concept of an array. Arrays seem like simple things but there are many questions that must be answered when adding them to a language, such as:
- fixed-size or variable-size?
- is the size part of the type?
- what do multidimensional arrays look like?
- does the empty array have meaning?
The answers to these questions affect whether arrays are just a feature of the language or a core part of its design.
In the early development of Go, it took about a year to decide the answers to these questions before the design felt right. The key step was the introduction of slices , which built on fixed-size arrays to give a flexible, extensible data structure. To this day, however, programmers new to Go often stumble over the way slices work, perhaps because experience from other languages has colored their thinking.
In this post we’ll attempt to clear up the confusion. We’ll do so by building up the pieces to explain how the append built-in function works, and why it works the way it does.
Arrays are an important building block in Go, but like the foundation of a building they are often hidden below more visible components. We must talk about them briefly before we move on to the more interesting, powerful, and prominent idea of slices.
Arrays are not often seen in Go programs because the size of an array is part of its type, which limits its expressive power.
The declaration
declares the variable buffer , which holds 256 bytes. The type of buffer includes its size, [256]byte . An array with 512 bytes would be of the distinct type [512]byte .
The data associated with an array is just that: an array of elements. Schematically, our buffer looks like this in memory,
That is, the variable holds 256 bytes of data and nothing else. We can access its elements with the familiar indexing syntax, buffer[0] , buffer[1] , and so on through buffer[255] . (The index range 0 through 255 covers 256 elements.) Attempting to index buffer with a value outside this range will crash the program.
There is a built-in function called len that returns the number of elements of an array or slice and also of a few other data types. For arrays, it’s obvious what len returns. In our example, len(buffer) returns the fixed value 256.
Arrays have their place—they are a good representation of a transformation matrix for instance—but their most common purpose in Go is to hold storage for a slice.
Slices: The slice header
Slices are where the action is, but to use them well one must understand exactly what they are and what they do.
A slice is a data structure describing a contiguous section of an array stored separately from the slice variable itself. A slice is not an array . A slice describes a piece of an array.
Given our buffer array variable from the previous section, we could create a slice that describes elements 100 through 150 (to be precise, 100 through 149, inclusive) by slicing the array:
In that snippet we used the full variable declaration to be explicit. The variable slice has type []byte , pronounced “slice of bytes”, and is initialized from the array, called buffer , by slicing elements 100 (inclusive) through 150 (exclusive). The more idiomatic syntax would drop the type, which is set by the initializing expression:
Inside a function we could use the short declaration form,
What exactly is this slice variable? It’s not quite the full story, but for now think of a slice as a little data structure with two elements: a length and a pointer to an element of an array. You can think of it as being built like this behind the scenes:
Of course, this is just an illustration. Despite what this snippet says that sliceHeader struct is not visible to the programmer, and the type of the element pointer depends on the type of the elements, but this gives the general idea of the mechanics.
So far we’ve used a slice operation on an array, but we can also slice a slice, like this:
Just as before, this operation creates a new slice, in this case with elements 5 through 9 (inclusive) of the original slice, which means elements 105 through 109 of the original array. The underlying sliceHeader struct for the slice2 variable looks like this:
Notice that this header still points to the same underlying array, stored in the buffer variable.
We can also reslice , which is to say slice a slice and store the result back in the original slice structure. After
the sliceHeader structure for the slice variable looks just like it did for the slice2 variable. You’ll see reslicing used often, for example to truncate a slice. This statement drops the first and last elements of our slice:
[Exercise: Write out what the sliceHeader struct looks like after this assignment.]
You’ll often hear experienced Go programmers talk about the “slice header” because that really is what’s stored in a slice variable. For instance, when you call a function that takes a slice as an argument, such as bytes.IndexRune , that header is what gets passed to the function. In this call,
the slice argument that is passed to the IndexRune function is, in fact, a “slice header”.
There’s one more data item in the slice header, which we talk about below, but first let’s see what the existence of the slice header means when you program with slices.
Passing slices to functions
It’s important to understand that even though a slice contains a pointer, it is itself a value. Under the covers, it is a struct value holding a pointer and a length. It is not a pointer to a struct.
This matters.
When we called IndexRune in the previous example, it was passed a copy of the slice header. That behavior has important ramifications.
Consider this simple function:
It does just what its name implies, iterating over the indices of a slice (using a for range loop), incrementing its elements.
(You can edit and re-execute these runnable snippets if you want to explore.)
Even though the slice header is passed by value, the header includes a pointer to elements of an array, so both the original slice header and the copy of the header passed to the function describe the same array. Therefore, when the function returns, the modified elements can be seen through the original slice variable.
The argument to the function really is a copy, as this example shows:
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. The slice variable is unchanged but the returned value has the new length, which is then stored in newSlice .
Pointers to slices: Method receivers
Another way to have a function modify the slice header is to pass a pointer to it. Here’s a variant of our previous example that does this:
It seems clumsy in that example, especially dealing with the extra level of indirection (a temporary variable helps), but there is one common case where you see pointers to slices. It is idiomatic to use a pointer receiver for a method that modifies a slice.
Let’s say we wanted to have a method on a slice that truncates it at the final slash. We could write it like this:
If you run this example you’ll see that it works properly, updating the slice in the caller.
[Exercise: Change the type of the receiver to be a value rather than a pointer and run it again. Explain what happens.]
On the other hand, if we wanted to write a method for path that upper-cases the ASCII letters in the path (parochially ignoring non-English names), the method could be a value because the value receiver will still point to the same underlying array.
Here the ToUpper method uses two variables in the for range construct to capture the index and slice element. This form of loop avoids writing p[i] multiple times in the body.
[Exercise: Convert the ToUpper method to use a pointer receiver and see if its behavior changes.]
[Advanced exercise: Convert the ToUpper method to handle Unicode letters, not just ASCII.]
Look at the following function that extends its argument slice of ints by one element:
(Why does it need to return the modified slice?) Now run it:
See how the slice grows until… it doesn’t.
It’s time to talk about the third component of the slice header: its capacity . Besides the array pointer and length, the slice header also stores its capacity:
The Capacity field records how much space the underlying array actually has; it is the maximum value the Length can reach. Trying to grow the slice beyond its capacity will step beyond the limits of the array and will trigger a panic.
After our example slice is created by
its header looks like this:
The Capacity field is equal to the length of the underlying array, minus the index in the array of the first element of the slice (zero in this case). If you want to inquire what the capacity is for a slice, use the built-in function cap :
What if we want to grow the slice beyond its capacity? You can’t! By definition, the capacity is the limit to growth. But you can achieve an equivalent result by allocating a new array, copying the data over, and modifying the slice to describe the new array.
Let’s start with allocation. We could use the new built-in function to allocate a bigger array and then slice the result, but it is simpler to use the make built-in function instead. It allocates a new array and creates a slice header to describe it, all at once. The make function takes three arguments: the type of the slice, its initial length, and its capacity, which is the length of the array that make allocates to hold the slice data. This call creates a slice of length 10 with room for 5 more (15-10), as you can see by running it:
This snippet doubles the capacity of our int slice but keeps its length the same:
After running this code the slice has much more room to grow before needing another reallocation.
When creating slices, it’s often true that the length and capacity will be same. The make built-in has a shorthand for this common case. The length argument defaults to the capacity, so you can leave it out to set them both to the same value. After
the gophers slice has both its length and capacity set to 10.
When we doubled the capacity of our slice in the previous section, we wrote a loop to copy the old data to the new slice. Go has a built-in function, copy , to make this easier. Its arguments are two slices, and it copies the data from the right-hand argument to the left-hand argument. Here’s our example rewritten to use copy :
The copy function is smart. It only copies what it can, paying attention to the lengths of both arguments. In other words, the number of elements it copies is the minimum of the lengths of the two slices. This can save a little bookkeeping. Also, copy returns an integer value, the number of elements it copied, although it’s not always worth checking.
The copy function also gets things right when source and destination overlap, which means it can be used to shift items around in a single slice. Here’s how to use copy to insert a value into the middle of a slice.
There are a couple of things to notice in this function. First, of course, it must return the updated slice because its length has changed. Second, it uses a convenient shorthand. The expression
means exactly the same as
Also, although we haven’t used the trick yet, we can leave out the first element of a slice expression too; it defaults to zero. Thus
just means the slice itself, which is useful when slicing an array. This expression is the shortest way to say “a slice describing all the elements of the array”:
Now that’s out of the way, let’s run our Insert function.
Append: An example
A few sections back, we wrote an Extend function that extends a slice by one element. It was buggy, though, because if the slice’s capacity was too small, the function would crash. (Our Insert example has the same problem.) Now we have the pieces in place to fix that, so let’s write a robust implementation of Extend for integer slices.
In this case it’s especially important to return the slice, since when it reallocates the resulting slice describes a completely different array. Here’s a little snippet to demonstrate what happens as the slice fills up:
Notice the reallocation when the initial array of size 5 is filled up. Both the capacity and the address of the zeroth element change when the new array is allocated.
With the robust Extend function as a guide we can write an even nicer function that lets us extend the slice by multiple elements. To do this, we use Go’s ability to turn a list of function arguments into a slice when the function is called. That is, we use Go’s variadic function facility.
Let’s call the function Append . For the first version, we can just call Extend repeatedly so the mechanism of the variadic function is clear. The signature of Append is this:
What that says is that Append takes one argument, a slice, followed by zero or more int arguments. Those arguments are exactly a slice of int as far as the implementation of Append is concerned, as you can see:
Notice the for range loop iterating over the elements of the items argument, which has implied type []int . Also notice the use of the blank identifier _ to discard the index in the loop, which we don’t need in this case.
Another new technique in this example is that we initialize the slice by writing a composite literal, which consists of the type of the slice followed by its elements in braces:
The Append function is interesting for another reason. Not only can we append elements, we can append a whole second slice by “exploding” the slice into arguments using the ... notation at the call site:
Of course, we can make Append more efficient by allocating no more than once, building on the innards of Extend :
Here, notice how we use copy twice, once to move the slice data to the newly allocated memory, and then to copy the appending items to the end of the old data.
Try it; the behavior is the same as before:
Append: The built-in function
And so we arrive at the motivation for the design of the append built-in function. It does exactly what our Append example does, with equivalent efficiency, but it works for any slice type.
A weakness of Go is that any generic-type operations must be provided by the run-time. Some day that may change, but for now, to make working with slices easier, Go provides a built-in generic append function. It works the same as our int slice version, but for any slice type.
Remember, since the slice header is always updated by a call to append , you need to save the returned slice after the call. In fact, the compiler won’t let you call append without saving the result.
Here are some one-liners intermingled with print statements. Try them, edit them and explore:
It’s worth taking a moment to think about the final one-liner of that example in detail to understand how the design of slices makes it possible for this simple call to work correctly.
There are lots more examples of append , copy , and other ways to use slices on the community-built “Slice Tricks” Wiki page .
As an aside, with our newfound knowledge we can see what the representation of a nil slice is. Naturally, it is the zero value of the slice header:
The key detail is that the element pointer is nil too. The slice created by
has length zero (and maybe even capacity zero) but its pointer is not nil , so it is not a nil slice.
As should be clear, an empty slice can grow (assuming it has non-zero capacity), but a nil slice has no array to put values in and can never grow to hold even one element.
That said, a nil slice is functionally equivalent to a zero-length slice, even though it points to nothing. It has length zero and can be appended to, with allocation. As an example, look at the one-liner above that copies a slice by appending to a nil slice.
Now a brief section about strings in Go in the context of slices.
Strings are actually very simple: they are just read-only slices of bytes with a bit of extra syntactic support from the language.
Because they are read-only, there is no need for a capacity (you can’t grow them), but otherwise for most purposes you can treat them just like read-only slices of bytes.
For starters, we can index them to access individual bytes:
We can slice a string to grab a substring:
It should be obvious now what’s going on behind the scenes when we slice a string.
We can also take a normal slice of bytes and create a string from it with the simple conversion:
and go in the reverse direction as well:
The array underlying a string is hidden from view; there is no way to access its contents except through the string. That means that when we do either of these conversions, a copy of the array must be made. Go takes care of this, of course, so you don’t have to. After either of these conversions, modifications to the array underlying the byte slice don’t affect the corresponding string.
An important consequence of this slice-like design for strings is that creating a substring is very efficient. All that needs to happen is the creation of a two-word string header. Since the string is read-only, the original string and the string resulting from the slice operation can share the same array safely.
A historical note: The earliest implementation of strings always allocated, but when slices were added to the language, they provided a model for efficient string handling. Some of the benchmarks saw huge speedups as a result.
There’s much more to strings, of course, and a separate blog post covers them in greater depth.
To understand how slices work, it helps to understand how they are implemented. There is a little data structure, the slice header, that is the item associated with the slice variable, and that header describes a section of a separately allocated array. When we pass slice values around, the header gets copied but the array it points to is always shared.
Once you appreciate how they work, slices become not only easy to use, but powerful and expressive, especially with the help of the copy and append built-in functions.
More reading
There’s lots to find around the intertubes about slices in Go. As mentioned earlier, the “Slice Tricks” Wiki page has many examples. The Go Slices blog post describes the memory layout details with clear diagrams. Russ Cox’s Go Data Structures article includes a discussion of slices along with some of Go’s other internal data structures.
There is much more material available, but the best way to learn about slices is to use them.
- Data Types in Go
- Go Keywords
- Go Control Flow
- Go Functions
- GoLang Structures
- GoLang Arrays
- GoLang Strings
- GoLang Pointers
- GoLang Interface
- GoLang Concurrency
How to copy one slice into another slice in Golang?
A Slice is a variable-length sequence that stores elements of a similar type, you are not allowed to store different type of elements in the same slice. In Slice, you can copy one slice into another slice using the copy() function provided by the Go language. Or in other words, copy() function allows you to copy the elements of one slice into another slice.
Here, dst represents the destination slice and src represents the source slice. It will return the number of elements copied that will be the minimum of len(dst) or len(src) . Let us discuss with the help of the given example:
Example 1:
Explanation: In the above example we have four integer type slices and we perform copy operation on them:
- copy_1:= copy(slc2, slc1): Here, slc2 is the destination slice and slc1 is the source slice. Here, slc2 is the nil slice when we try to copy slc1 slice in slc2 slice, then copy method will return the minimum of length of source and destination slice which is zero for empty slice slc2.
- copy_2:= copy(slc3, slc1): Here, slc3 is the destination slice and slc1 is the source slice. Here, the slc3 slice is the empty slice, so when we try to copy slc1 slice into slc3 using copy() function it copies only 5 elements from the slc1 slice starting from index 0 because the length of this slice is 5, so it can not store elements greater than 5.
- copy_3:= copy(slc4, slc1): Here, slc4 is the destination slice and slc1 is the source slice. When we try to copy slc1 slice into slc4 slice using copy() function it copies only 4 elements in it starting from index 0. Because the length of the slc4 slice is 4, so it can not store elements more than 4.
- copy_4:= copy(slc1, slc4): Here, you might be confused after its output. See, slc4 has been updated in the above line of code. So now consider the updated values for slc4 . So now slc4 has 4 elements and slc1 has 9 elements. So, the total number of elements that will be copied is 4.
Example 2:
To copy one slice into another slice in Go, you can use the built-in function copy. The copy function takes two arguments: the destination slice and the source slice, and it copies the elements from the source slice into the destination slice.
Here’s an example that demonstrates how to copy one slice into another slice in Go:
Source: [1 2 3 4 5] Destination: [1 2 3 4 5]
In this example, the slice source is created with the elements 1, 2, 3, 4, 5. The slice destination is created with the same length as the slice source by calling the function make, and it’s initialized to 0. The function copy is then called with destination and source as arguments to copy the elements from source into destination.
It’s important to note that the length of the destination slice must be equal to or greater than the length of the source slice, otherwise the copy function will panic. If the destination slice is longer than the source slice, the remaining elements in the destination slice will be unchanged.
Slice copying is a common operation in Go, and the copy function provides a simple and efficient way to copy slices. It’s widely used in Go programs to make copies of slices for various purposes.
Please Login to comment...
Similar reads.
- Go Language
- Golang-Slices
- How to Get a Free SSL Certificate
- Best SSL Certificates Provider in India
- Elon Musk's xAI releases Grok-2 AI assistant
- What is OpenAI SearchGPT? How it works and How to Get it?
- Content Improvement League 2024: From Good To A Great Article
Improve your Coding Skills with Practice
What kind of Experience do you want to share?
- 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
Golang Tutorial
- Golang - Home
- Golang - Keywords
- Golang - Data Types
- Golang - Variables
- Golang - Constants
- Golang - Program Structure
- Golang - Comments
- Golang - Naming a variable
- Golang - Shorthand for Defining Multiple Variables/Constants
- Golang - Find Type of Variable
- Golang - Binary Literals
- Golang - Octal Literals
- Golang - Hexadecimal Literals
- Golang - Type Casting or Type Conversion
- Golang – Operators
- Golang - Arithmetic Operators
- Golang - Relational Operators
- Golang - Logical Operators
- Golang - Bitwise Operators
- Golang - Assignment Operators
- Golang - Miscellaneous Operators
- Golang - if statement
- Golang - if-else statement
- Golang - if-else-if ladder statement
- Golang - nested if statement
- Golang - switch statement
- Golang - select statement
- Golang - Loops
- Golang - Functions
- Golang - 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
- Golang - Basic Programs
- Golang - Switch Statement Programs
- Golang - goto Statement Programs
- Golang - Looping Programs
- Golang - Array Programs
- Golang - String Programs
- Golang - Structure Programs
- Golang - User-defined Function Programs
- Golang - Variadic Function Programs
- Golang - Recursion Programs
- Golang - Pointer Programs
- Golang - Conversion Programs
- Golang - Slices Programs
- Golang - Date & Time Programs
- Golang - Timers & Tickers Programs
- Golang - Goroutines Programs
- Golang - Reflection Programs
- Golang - Maps Programs
- Golang - Range Programs
- Golang - Channels Programs
- Golang - File Handling Programs
- Golang - Buffered Input-Output Programs
- Golang - Command-line Arguments Programs
- Golang - Regular Expressions Programs
- Golang - JSON Programs
- Golang - os Package Programs
- Golang - strconv Package Programs
- Golang - log Package Programs
- Golang - math/rand Package Programs
- Golang - math/bits Package Programs
- Golang - sync/atomic Package Programs
- Golang - path/filepath Package Programs
- Golang - syscall Package Programs
- Golang - Signals Programs
- Golang - Shell Script Programs
Golang Practice
- Golang Interview Questions
- Golang Find Output Programs
- Golang Aptitude Questions
Golang Miscellaneous
- Golang - Print Boolean Value
- Golang - Print Double-quoted String
- Golang - Convert From Int to Binary
- Golang - Convert From Int to Octal
- Golang - Convert From Int to Hex
- Golang - Check if Structure is Empty
- Golang - Check if Key Exists in Map
- Golang - Return an Error
- Golang - new() & make() Functions
Advertisement
Home » Golang » Golang Reference
Golang copy() Function with Examples
Golang | copy() Function : Here, we are going to learn about the built-in copy() function with its usages, syntax, and examples. Submitted by IncludeHelp , on October 14, 2021 [Last updated : March 15, 2023]
copy() Function
In the Go programming language, the copy() is a built-in function that is used to copy the elements from a source slice into a destination slice and returns the number of copied elements copied.
It accepts two parameters ( dst , src []Type) and returns the number of copied elements.
Parameter(s)
- dst : The destination slice in which we have to copy the elements.
- src : The source slice whose elements are to be copied.
Return Value
The return type of the copy() function is an int , it returns the total number of copied elements.
Golang builtin Package »
Related Tutorials
- Golang true, false Constants
- Golang iota Constant
- Golang nil Variable
- Golang append() Function
- Golang cap() Function
- Golang close() Function
- Golang complex() Function
- Golang delete() Function
- Golang imag() Function
- Golang len() Function
- Golang make() Function
- Golang new() Function
- Golang panic() Function
- Golang print() Function
- Golang println() Function
- Golang real() Function
- Golang recover() Function
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.
Exploring Software
Shallow copy and deep copy in go.
Update: Please note that my usage of “Call by reference” is not correct here as pointed out by a Reddit community member here . Read it as “Call by passing pointer values”. While trying to understand this a bit more, I came across a few more links to the topic. You can find them here in my reply. I will do a follow up post on this topic perhaps. I am grateful that the reddit commenter helped me revise my mental model on the topic.
A shallow copy of an variable/object is a copy of an object, usually a container - for example, an array or a struct type such that the elements in both the copy and the original object are occupying the same memory addresses .
(PS: I am not sure, but perhaps, Rust does this differently where moving a basic data type is a shallow copy? I may be completely wrong).
A deep copy of an variable/object is a copy of an object and it’s containing elements (if any) such that the copy and the original object occupy different memory addresses .
This post is about shallow and deep copy in Go as it applies to various data types. As it turns out, for certain data types, a deep copy is the default, while as for others shallow copy is the default.
Let’s go exploring!
Call by Value and Call by reference
Basic data types.
Let’s consider two of the basic data types - int and string .
Consider the following program: ( Link to Playground )
We declare n1 and s1 as an integer and a string, respectively with each having an initial value. When we write the statement, n2 := n1 , we are creating a new integer variable, n2 . Similarly, s2 := s1 creates a new string variable. When we create these variables, their values are the same as that referred to by n1 and s1 , respectively. Then, when we update the values, the changes remain confined to the variables we are updating - i.e. updating the value of n2 doesn’t affect the value of n1 and vice-versa.
Hence, when we run the above code, you will see an output as follows:
The values of the form 0x are the memory address of a variable, n1 obtained by the statement, &n1 and printed using the %p verb.
Next, let’s update the code above to understand what happens when we call functions passing integers and strings as arguments.
Consider the following program ( Link to Playground ):
We define two functions, callByValue() and callByReference() . Both the functions accept two integers and two strings as parameters. In each function, we then proceed to update the values of the variables that the functions were called with. The difference is that the first function accepts the values of existing integer and string variables, and the second function accepts the memory address or pointer to variables containing integer and string values.
When we run the program, we will see the following output:
The changes made in the callByValue() function are not visible in the main() function. However, the changes made inside the second function are.
When we called the callByValue() function, we created a new set of variables containing the values of the existing variables. Hence, we changed the values of these new variables, the values of the existing variables which were defined in main() are not affected. On the other hand, when calling callByReference() , we passed along the memory addresses of the variables called in main() function. Hence, any updations to the values of those variables, affected the original variables, since they are manipulating the same memory address.
When it comes to the basic types, numbers and strings, it is always deep copy .
There is no shallow copy when it comes to these types. Another way of saying that is that, when we want a shallow copy , use memory addresses for basic data types.
Next, let’s explore what happens when we have basic data types as elements in a slice.
Slice of integers and strings
Consider the following program: ( Link to playground )
We define a slice, n1 containing integers. We create a new slice, n2 using the statement, n2 := n1 .
We then, update the value of the first element of n2 using n2[0] = n2[0]*10 .
At this stage when we print the two slices, we see that the first element of both the slices, n1 and n2 has been updated. This is because when we created a copy of n1 , it performed a shallow copy . Hence, even though the memory addresses of n1 and n2 are different, as you will soon see in the output, the elements they contain were pointing to the same underlying memory addresses .
Then, when we create a new slice and assign it to n2 , we have now overwritten the elements of the slice, n2 . Hence, the slice, n1 is not affected.
The same behavior is seen for the slice of strings, s1 and s2 .
When we run the program, you will see the following output:
How does the above translate to passing slices as function arguments?
When it comes to a slice, we are always working with shallow copies. Hence, there is no need for a call by reference when it comes to slices. Simply passing the slice is a call by reference. This playground link has an example.
When it comes to a slice of basic data types, we are by default working with a shallow copy . If you want to create a deep copy , you will find the copy() function useful. See here for an example of creating a deep copy of a slice.
Why do we have the default behavior copying a slice as a shallow copy? The Effective Go guide has the answer :
Slices hold references to an underlying array, and if you assign one slice to another, both refer to the same array. If a function takes a slice argument, changes it makes to the elements of the slice will be visible to the caller, analogous to passing a pointer to the underlying array.
So, we should expect the behavior to be different when it comes to arrays then? Let’s find out.
Arrays of strings and integers
Consider the following program. Compared to the previous program, we use arrays instead of slices ( Link to the playground ):
We define an array, n1 containing integers. We create a new array, n2 using the statement, n2 := n1 .
At this stage when we print the two arrays, we see that the first element of only the second array has been updated. n1 hasn’t been affected. Thus, we are working with a deep copy of our original array.
Then, when we create a new array and assign it to n2 , we have now overwritten the elements of the array, n2 . Hence, once again the array, n1 is not affected.
The same behavior is seen for the array of strings, s1 and s2 .
When it comes to arrays, we are always working with deep copies. Hence, if we want to pass an array which we want to modify in another function and want the updated result to be reflected in the original array, we should pass the array by reference. This playground link has an example.
It looks like this:
In the callByReference() function, we can see how Go allows us to work with the individual elements as if we are working with an array variable, n1 and not a pointer to an array, n1.
The default behavior of copying arrays is thus, deep copy. If your intention is to have a shallow copy, you will need to work with a pointer to an array, rather than a copy of the array, such as inside a function.
Elements in Maps and Struct types
A map and struct types are special as their elements are either one of the basic types, or an array or a slice of basic types. Thus, in the context of shallow and deep copy, we have two questions we want to explore:
- What is the behavior of the map and struct type itself?
- What is the behavior of the elements which are values of the struct or the map ?
The answer for a map is described in Effective Go :
Like slices, maps hold references to an underlying data structure. If you pass a map to a function that changes the contents of the map, the changes will be visible in the caller.
Thus, the default behavior of a map is a shallow copy.
A struct is by default deep copied. However, consider what happens to the constituent elements as we explore our second question above next.
A map is by default shallow copied and hence automatically, the keys and values are shallow copied too, irrespective of the behavior of the constituent elements.
For a value of a struct type, the behavior of the constituent elements is preserved as it would be when they are not elements of a struct type. That is:
- An integer, a string and an array will be deep copied when a struct or a map is copied
- A slice will be shallow copied when a struct or a map is copied.
Consider the following code for an example ( link to playground ):
We define a struct type:
Both the callByValue() and callByReference() function performs the same modifications to the struct value. However, only the changes to the arr1 element performed in the callByValue() function is reflected in main() . The change to arr is not reflected as we would expect based on our previous discussions. Of course, when we then invoke the callByReference() function, all the three changes are reflected in main() , as we are working with a pointer to the struct value.
Running the above program will show the following output:
This article specifically discusses a bit more around why you should never shallow copy a struct type. The author makes an interesting observation (Quoted) - “structs evolve over time”. Thus, it’s a better idea to adopt deep copying from the get go than getting bugged later and adopting it.
A map is by default call by reference and struct is by default call by value. Hence, if you want call by value behavior:
- map : Create a deep copy by creating a new map and copying the key value pairs. Be careful of also ensuring that you deep copy the elements themselves
- struct : Create a deep copy by creating a new struct and copying the elements. Be careful of also ensuring that you deep copy the elements themselves
If you want call by reference behavior:
- map : Nothing special to do, passing a map is by default call by reference
- struct : Pass a pointer to the struct value instead of the struct itself
A map is shallow copied and a struct is deep copied, but the elements of the struct may be deep copied or shallow copied depending on their own default behavior.
As we have seen in this post (or let’s say as I learned while writing this post), whether a value in Go is shallow copied or deep copied varies. It has to do with the internal representation of the data type itself. Hence, the Effective Go refers to values of type map, a slices or a channel as a reference to an underlying data structure. The section Allocation with make describes this behavior. We haven’t looked at channels in this post (may be in future), but they also belong to the same category of maps and slices and hence should exhibit similar behavior.
A “surprise” for me that I had to overlook when I was working on this post was that shallow copies had different memory addresses, as printed by the %p verb. When I first came across the shallow copy behavior in some code (the contributing cause to writing this post), I was perplexed since the memory addresses was different. That remains an unanswered question for me, for now.
I explored the same topic in C and Python programming languages several years back:
- Date in CPython
- Data in C and CPython
- Effective Go
- Basic Types
- Understanding Data Types in Go
- Shallow copy of a go struct in golang is never a good idea
- Trending Categories
- Selected Reading
- UPSC IAS Exams Notes
- Developer's Best Practices
- Questions and Answers
- Effective Resume Writing
- HR Interview Questions
- Computer Glossary
Copy an Array by Value and Reference into Another Array in Golang
In Golang, arrays are fixed-size data structures that hold a collection of values of the same type. In some cases, it may be necessary to copy an array to another array either by value or by reference. In this article, we will explore how to copy an array in Golang both by value and by reference.
Copying an Array by Value in Golang
In Golang, when you assign an array to another variable or pass it as a parameter to a function, it is copied by value. This means that any changes made to the copied array will not affect the original array. To copy an array by value, you can simply assign it to a new variable.
Here's an example of copying an array by value in Golang −
In the above example, we created an array arr1 with three elements and assigned it to arr2 by value. Then, we modified the first element of arr2 to 0. However, this modification did not affect arr1 as it was copied by value.
Copying an Array by Reference in Golang
In Golang, arrays are copied by reference when they are passed to a function as a slice. A slice is a dynamically-sized, flexible view into the elements of an array. When you pass an array to a function as a slice, it is passed by reference, which means that any changes made to the slice will also affect the original array.
Here's an example of copying an array by reference in Golang −
In the above example, we created an array arr1 with three elements and created a slice slice1 from it. We then passed slice1 to the modifySlice function, which modified the first element of slice1 to 0. Since slice1 was created from arr1 by reference, this modification also affected arr1.
Copying Part of an Array in Golang
In Golang, you can also copy part of an array to another array or slice using the copy function. The copy function takes two arguments: the destination array or slice and the source array or slice. It then copies the elements from the source to the destination.
Here's an example of copying part of an array in Golang −
In this example, we first create an array arr1 with 5 elements. Then we create an empty array arr2 of length 3. We also create a slice slice1 from arr1 containing elements from index 1 to 3 (excluding the element at index 3).
We then use the copy() function to copy slice1 into arr2. Since arr2 has a length of 3, only the first 3 elements of slice1 are copied into it.
Finally, we print the contents of arr1, arr2, and slice1 to verify that the copy was successful.
In the output, we can see that arr2 contains the elements [2 3 4], which are the elements copied from slice1.
Arrays are an important data structure in Golang and knowing how to copy them by value and reference is essential for any developer working with arrays. It is important to understand the difference between copying an array by value and copying by reference, as they have different implications and use cases. Copying an array by value creates a new copy of the array, whereas copying by reference creates a new reference to the same underlying array. Additionally, developers should also be familiar with how to copy parts of an array into another array using the built-in copy() function. With these techniques, developers can effectively work with arrays in Golang and create efficient and reliable code.
- Related Articles
- Golang Program To Push An Array Into Another Array
- How to copy a section of an array into another array in C#?
- Python program to copy all elements of one array into another array
- Golang Program To Copy All The Elements Of One Array To Another Array
- Swift: Pass an array by reference?
- Check whether an array can fit into another array by rearranging the elements in the array
- How to Copy Struct Type Using Value and Pointer Reference in Golang?
- C++ Program to push an array into another array
- Python Program to push an array into another array
- How to copy one slice into another slice in Golang?
- Golang program to copy one file into another file
- How to pass an array by reference in C++
- Golang Program To Append An Element Into An Array
- Compute the truth value of an array AND to another array element-wise in Numpy
- What are the different ways of copying an array into another array in Java?
Kickstart Your Career
Get certified by completing the course
Strings in Go
Like many other programming languages, string is also one important kind of types in Go. This article will list all the facts of strings.
The Internal Structure of String Types
From the declaration, we know that a string is actually a byte sequence wrapper. In fact, we can really view a string as an (element-immutable) byte slice.
Note, in Go, byte is a built-in alias of type uint8 .
Some Simple Facts About Strings
- String values can be used as constants (along with boolean and all kinds of numeric values).
- Go supports two styles of string literals , the double-quote style (or interpreted literals) and the back-quote style (or raw string literals).
- The zero values of string types are blank strings, which can be represented with "" or `` in literal.
- Strings can be concatenated with + and += operators.
- String types are all comparable (by using the == and != operators). And like integer and floating-point values, two values of the same string type can also be compared with > , < , >= and <= operators. When comparing two strings, their underlying bytes will be compared, one byte by one byte. If one string is a prefix of the other one and the other one is longer, then the other one will be viewed as the larger one.
- Like Java, the contents (underlying bytes) of string values are immutable. The lengths of string values also can't be modified separately. An addressable string value can only be overwritten as a whole by assigning another string value to it.
- use functions provided in the strings standard package to do all kinds of string manipulations.
- call the built-in len function to get the length of a string (number of bytes stored in the string).
- use the element access syntax aString[i] introduced in container element accesses to get the i th byte value stored in aString . The expression aString[i] is not addressable. In other words, value aString[i] can't be modified.
- use the subslice syntax aString[start:end] to get a substring of aString . Here, start and end are both indexes of bytes stored in aString .
- For the standard Go compiler, the destination string variable and source string value in a string assignment will share the same underlying byte sequence in memory. The result of a substring expression aString[start:end] also shares the same underlying byte sequence with the base string aString in memory.
About why variables a and b are evaluated to different values, please read the special type deduction rule in bitwise shift operator operation and which function calls are evaluated at compile time 。
String Encoding and Unicode Code Points
Unicode standard specifies a unique value for each character in all kinds of human languages. But the basic unit in Unicode is not character, it is code point instead. For most code points, each of them corresponds to a character, but for a few characters, each of them consists of several code points.
Code points are represented as rune values in Go. In Go, rune is a built-in alias of type int32 .
In applications, there are several encoding methods to represent code points, such as UTF-8 encoding and UTF-16 encoding. Nowadays, the most popularly used encoding method is UTF-8 encoding. In Go, all string constants are viewed as UTF-8 encoded. At compile time, illegal UTF-8 encoded string constants will make compilation fail. However, at run time, Go runtime can't prevent some strings from being illegally UTF-8 encoded.
For UTF-8 encoding, each code point value may be stored as one or more bytes (up to four bytes). For example, each English code point (which corresponds to one English character) is stored as one byte, however each Chinese code point (which corresponds to one Chinese character) is stored as three bytes.
String Related Conversions
In the article constants and variables , we have learned that integers can be explicitly converted to strings (but not vice versa).
- a string value can be explicitly converted to a byte slice, and vice versa. A byte slice is a slice with element type's underlying type as []byte .
- a string value can be explicitly converted to a rune slice, and vice versa. A rune slice is a slice whose element type's underlying type as []rune .
In a conversion from a rune slice to string, each slice element (a rune value) will be UTF-8 encoded as from one to four bytes and stored in the result string. If a slice rune element value is outside the range of valid Unicode code points, then it will be viewed as 0xFFFD , the code point for the Unicode replacement character. 0xFFFD will be UTF-8 encoded as three bytes ( 0xef 0xbf 0xbd ).
When a string is converted to a rune slice, the bytes stored in the string will be viewed as successive UTF-8 encoding byte sequence representations of many Unicode code points. Bad UTF-8 encoding representations will be converted to a rune value 0xFFFD .
When a string is converted to a byte slice, the result byte slice is just a deep copy of the underlying byte sequence of the string. When a byte slice is converted to a string, the underlying byte sequence of the result string is also just a deep copy of the byte slice. A memory allocation is needed to store the deep copy in each of such conversions. The reason why a deep copy is essential is slice elements are mutable but the bytes stored in strings are immutable, so a byte slice and a string can't share byte elements.
- illegal UTF-8 encoded bytes are allowed and will keep unchanged.
- the standard Go compiler makes some optimizations for some special cases of such conversions, so that the deep copies are not made. Such cases will be introduced below.
- use string values as a hop. This way is convenient but not very efficient, for two deep copies are needed in the process.
- use the functions in unicode/utf8 standard package.
- use the Runes function in the bytes standard package to convert a []byte value to a []rune value. There is not a function in this package to convert a rune slice to byte slice.
Compiler Optimizations for Conversions Between Strings and Byte Slices
- a conversion (from string to byte slice) which follows the range keyword in a for-range loop.
- a conversion (from byte slice to string) which is used as a map key in map element retrieval indexing syntax.
- a conversion (from byte slice to string) which is used in a comparison.
- a conversion (from byte slice to string) which is used in a string concatenation, and at least one of concatenated string values is a non-blank string constant.
Note, the last line might not output value if there are data races in evaluating string(key) . However, such data races will never cause panics.
for-range on Strings
The for-range loop control flow applies to strings. But please note, for-range will iterate the Unicode code points (as rune values), instead of bytes, in a string. Bad UTF-8 encoding representations in the string will be interpreted as rune value 0xFFFD .
- the iteration index value may be not continuous. The reason is the index is the byte index in the ranged string and one code point may need more than one byte to represent.
- the first character, é , is composed of two runes (3 bytes total)
- the second character, क्षि , is composed of four runes (12 bytes total).
- the English character, a , is composed of one rune (1 byte).
- the character, π , is composed of one rune (2 bytes).
- the Chinese character, 囧 , is composed of one rune (3 bytes).
From the above several examples, we know that len(s) will return the number of bytes in string s . The time complexity of len(s) is O (1) . How to get the number of runes in a string? Using a for-range loop to iterate and count all runes is a way, and using the RuneCountInString function in the unicode/utf8 standard package is another way. The efficiencies of the two ways are almost the same. The third way is to use len([]rune(s)) to get the count of runes in string s . Since Go Toolchain 1.11, the standard Go compiler makes an optimization for the third way to avoid an unnecessary deep copy so that it is as efficient as the former two ways. Please note that the time complexities of these ways are all O (n) .
More String Concatenation Methods
- The Sprintf / Sprint / Sprintln functions in the fmt standard package can be used to concatenate values of any types, including string types.
- Use the Join function in the strings standard package.
- The Buffer type in the bytes standard package (or the built-in copy function) can be used to build byte slices, which afterwards can be converted to string values.
- Since Go 1.10, the Builder type in the strings standard package can be used to build strings. Comparing with bytes.Buffer way, this way avoids making an unnecessary duplicated copy of underlying bytes for the result string.
The standard Go compiler makes optimizations for string concatenations by using the + operator. So generally, using + operator to concatenate strings is convenient and efficient if all of the concatenated strings may present in a concatenation statement.
Sugar: Use Strings as Byte Slices
From the article arrays, slices and maps , we have learned that we can use the built-in copy and append functions to copy and append slice elements. In fact, as a special case, if the first argument of a call to either of the two functions is a byte slice, then the second argument can be a string (if the call is an append call, then the string argument must be followed by three dots ... ). In other words, a string can be used as a byte slice for the special case.
More About String Comparisons
- For == and != comparisons, if the lengths of the compared two strings are not equal, then the two strings must be also not equal (no needs to compare their bytes).
- If their underlying byte sequence pointers of the compared two strings are equal, then the comparison result is the same as comparing the lengths of the two strings.
So for two equal strings, the time complexity of comparing them depends on whether or not their underlying byte sequence pointers are equal. If the two are equal, then the time complexity is O (1) , otherwise, the time complexity is O (n) , where n is the length of the two strings.
As above mentioned, for the standard Go compiler, in a string value assignment, the destination string value and the source string value will share the same underlying byte sequence in memory. So the cost of comparing the two strings becomes very small.
1ms is 1000000ns! So please try to avoid comparing two long strings if they don't share the same underlying byte sequence.
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 Layouts
- 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
- Getting started with Go
- Awesome Book
- Awesome Community
- Awesome Course
- Awesome Tutorial
- Awesome YouTube
- Base64 Encoding
- Best practices on project structure
- Build Constraints
- Concurrency
- Console I/O
- Cross Compilation
- Cryptography
- Developing for Multiple Platforms with Conditional Compiling
- Error Handling
- Executing Commands
- Getting Started With Go Using Atom
- HTTP Client
- HTTP Server
- Inline Expansion
- Installation
- JWT Authorization in Go
- Memory pooling
- Object Oriented Programming
- Panic and Recover
- Parsing Command Line Arguments And Flags
- Parsing CSV files
- Profiling using go tool pprof
- Protobuf in Go
- Select and Channels
- Send/receive emails
- Anonymous struct
- Basic Declaration
- Composition and Embedding
- Empty struct
- Exported vs. Unexported Fields (Private vs Public)
- Making struct copies.
- Struct Literals
- Text + HTML Templating
- The Go Command
- Type conversions
- Worker Pools
- Zero values
Go Structs Making struct copies.
Fastest entity framework extensions.
A struct can simply be copied using assignment.
In above case, 't' and 'u' are now separate objects (struct values).
Since T does not contain any reference types (slices, map, channels) as its fields, t and u above can be modified without affecting each other.
However, if T contains a reference type, for example:
Then a simple copy by assignment would copy the value of slice type field as well to the new object. This would result in two different objects referring to the same slice object.
Since both u and t refer to the same slice through their field xs updating a value in the slice of one object would reflect the change in the other.
Hence, extra care must be taken to ensure this reference type property does not produce unintended behavior.
To copy above objects for example, an explicit copy of the slice field could be performed:
Got any Go Question?
- Advertise with us
- Cookie Policy
- Privacy Policy
Get monthly updates about new articles, cheatsheets, and tricks.
Go by Example : Slices
are an important data type in Go, giving a more powerful interface to sequences than arrays. | |
main | |
( "fmt" "slices" ) | |
main() { | |
Unlike arrays, slices are typed only by the elements they contain (not the number of elements). An uninitialized slice equals to nil and has length 0. | s []string fmt.Println("uninit:", s, s == nil, len(s) == 0) |
To create an empty slice with non-zero length, use the builtin . Here we make a slice of s of length (initially zero-valued). By default a new slice’s capacity is equal to its length; if we know the slice is going to grow ahead of time, it’s possible to pass a capacity explicitly as an additional parameter to . | = make([]string, 3) fmt.Println("emp:", s, "len:", len(s), "cap:", cap(s)) |
We can set and get just like with arrays. | [0] = "a" s[1] = "b" s[2] = "c" fmt.Println("set:", s) fmt.Println("get:", s[2]) |
returns the length of the slice as expected. | .Println("len:", len(s)) |
In addition to these basic operations, slices support several more that make them richer than arrays. One is the builtin , which returns a slice containing one or more new values. Note that we need to accept a return value from as we may get a new slice value. | = append(s, "d") s = append(s, "e", "f") fmt.Println("apd:", s) |
Slices can also be ’d. Here we create an empty slice of the same length as and copy into from . | := make([]string, len(s)) copy(c, s) fmt.Println("cpy:", c) |
Slices support a “slice” operator with the syntax . For example, this gets a slice of the elements , , and . | := s[2:5] fmt.Println("sl1:", l) |
This slices up to (but excluding) . | = s[:5] fmt.Println("sl2:", l) |
And this slices up from (and including) . | = s[2:] fmt.Println("sl3:", l) |
We can declare and initialize a variable for slice in a single line as well. | := []string{"g", "h", "i"} fmt.Println("dcl:", t) |
The package contains a number of useful utility functions for slices. | := []string{"g", "h", "i"} if slices.Equal(t, t2) { fmt.Println("t == t2") } |
Slices can be composed into multi-dimensional data structures. The length of the inner slices can vary, unlike with multi-dimensional arrays. | := make([][]int, 3) for i := 0; i < 3; i++ { innerLen := i + 1 twoD[i] = make([]int, innerLen) for j := 0; j < innerLen; j++ { twoD[i][j] = i + j } } fmt.Println("2d: ", twoD) } |
Note that while slices are different types than arrays, they are rendered similarly by . | go run slices.go uninit: [] true true emp: [ ] len: 3 cap: 3 set: [a b c] get: c len: 3 apd: [a b c d e f] cpy: [a b c d e f] sl1: [c d e] sl2: [a b c d e] sl3: [c d e f] dcl: [g h i] t == t2 2d: [[0] [1 2] [2 3 4]] |
Check out this by the Go team for more details on the design and implementation of slices in Go. | |
Now that we’ve seen arrays and slices we’ll look at Go’s other key builtin data structure: maps. |
Next example: Maps .
by Mark McGranaghan and Eli Bendersky | source | license
Are Go variables passed as values always copied?
When I pass a variable by value in Go, am I always making a copy of the variable in memory, even if the function receiving this variable only reads (doesn’t mutate) the value? This is more a question about the compiled output than the language itself.
Does it make a difference if it’s a struct vs a string?
From what I’ve read, I get the impression Go always makes a copy of a variable passed by value. Say Go decided to implement a sort of copy-on-write optimisation for function parameters - would this cause any problems?
Take a look at the official FAQ, especially the section about Pointers and Allocation .
When are function parameters passed by value? As in all languages in the C family, everything in Go is passed by value. That is, a function always gets a copy of the thing being passed, as if there were an assignment statement assigning the value to the parameter. For instance, passing an int value to a function makes a copy of the int, and passing a pointer value makes a copy of the pointer, but not the data it points to. (See a later section for a discussion of how this affects method receivers.) Map and slice values behave like pointers: they are descriptors that contain pointers to the underlying map or slice data. Copying a map or slice value doesn’t copy the data it points to. Copying an interface value makes a copy of the thing stored in the interface value. If the interface value holds a struct, copying the interface value makes a copy of the struct. If the interface value holds a pointer, copying the interface value makes a copy of the pointer, but again not the data it points to. Note that this discussion is about the semantics of the operations. Actual implementations may apply optimizations to avoid copying as long as the optimizations do not change the semantics.
This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.
Learn to Clone or Copy Map in GO Like a PRO [5 Methods]
November 1, 2023
In the vast and versatile world of Go (Golang) programming, one often encounters the necessity to replicate data structures, ensuring that modifications in the replica do not affect the original. This necessity brings us to the concepts of copy map and clone map. When we talk about copying or cloning a map in Go, we refer to the creation of a duplicate map, possessing the same keys and values as the original. This duplication process is essential in various scenarios, such as preserving the original map’s state, preventing unauthorized modifications, or performing operations on the map without altering the actual data.
In this tutorial we will explore different methods which can be used to copy map or what we can also term as clone map in Golang.
Different Methods to Copy Map in GO
Here are various methods you can use to copy or clone a map in Go (Golang):
- Using Range Loop with Make : A simple method where a new map is initialized, and elements from the original map are iterated over and assigned to the new map, ensuring a separate memory allocation.
- Using JSON Marshalling and Unmarshalling : This technique involves converting the original map into a JSON object and then converting it back into a new map, ensuring that the new map is a separate entity.
- Using Deep Copy with Third-Party Packages : Utilizing available third-party packages that offer deep copy functionalities, ensuring that nested maps or maps with reference types are effectively cloned.
- Using Reflection : Employing the reflect package to dynamically create a new map and populate it with the elements of the original map, catering to various data types.
- Custom Implementation Based on Type : Crafting specific functions or methods tailored to the types stored in the map, ensuring an effective copying process tuned to the map's content.
Deep Copy vs Shallow Copy
When you copy map elements in Go, it’s essential to understand the difference between a deep copy and a shallow copy. The distinction lies in how the map's contents, particularly reference types like slices, maps, and pointers, are duplicated.
A deep copy creates a new map with not only the immediate elements of the original map being duplicated but also the underlying elements of any reference types recursively copied. This means that modifications to the nested elements in the copied map won’t affect the original map.
Situations where Deep Copy is applicable:
- When the map contains reference types like slices , maps, or pointers, and you want to manipulate them without affecting the original map.
- When you need complete isolation between the original and copied map.
Example of Deep Copy:
In this example, a deep copy of the map is made, and modifying the copied map does not affect the original map.
Shallow Copy
A shallow copy duplicates the immediate elements of the original map into a new map, but the reference types within the map still point to the same memory locations as those in the original map.
Situations where Shallow Copy is applicable:
- When you only need to modify the top-level elements of the map and want to maintain links to the original nested structures.
- When full isolation between the original and copied map is not necessary.
Example of Shallow Copy:
In this example, a shallow copy of the map is made. Modifying the copied map's slice also affects the original map because the slice's underlying array is still shared between both maps.
1. Using Range Loop with Make
When you want to copy map elements in Go, one straightforward approach is utilizing a range loop in conjunction with the make() function. Here’s how it works:
Performing Shallow Copy
In a shallow copy, the outermost objects are duplicated, while the inner elements retain references to the same memory locations as the original map’s inner elements.
Here, both the original and copied maps refer to the same memory locations of the inner elements. Modifying the copied map also modifies the original map.
Performing Deep Copy
A deep copy means cloning every item recursively, ensuring that no memory locations of the inner elements are shared between the original and copied map.
Modifying the copied map doesn’t affect the original map because every element, including the inner slices, has been recursively cloned.
We can also declare the copyMap logic inside a function to make it re-usable:
2. Using JSON Marshalling and Unmarshalling
JSON Marshalling and Unmarshalling can also be used to copy map contents in Go. This method involves converting the map into a JSON string (marshalling) and then creating a new map by parsing the JSON string back (unmarshalling). This is more commonly used for deep copying and it is not so straight forward to perform shallow copy using this method.
By default, using JSON marshalling and unmarshalling performs a deep copy because the data is serialized and deserialized into a new memory allocation .
3. Using Third-Party Package (copier)
The copier package primarily focuses on struct copying but can also be utilized for maps, primarily resulting in deep copying behavior. Here's how you can use copier for copying maps:
In this example, even though pointers are used, copier creates a new instance of the ValueStruct , making the copiedMap independent of the originalMap . This is essentially a deep copy, as modifications to the copiedMap do not affect the originalMap .
The copier package might not directly support a shallow copy in terms of retaining the original pointers. For a shallow copy, you might need to use a different approach or package.
4. Using Reflection
Reflection in Go is a powerful tool that allows for inspection and modification of variable types and values at runtime. It’s more complex and might not be the most performance-efficient way of copying maps, but it offers flexibility. Here’s how you could employ reflection to accomplish both shallow and deep copy tasks:
A shallow copy creates a new map, but the elements inside the map might still refer to the same memory locations as the original map.
A deep copy using reflection involves recursively copying each element, ensuring that the original and copied maps don’t share references.
In these examples, functions shallowCopyMap() and deepCopyMap() utilize reflection to perform the copy operations.
Note that using reflection might not be the most performant choice due to the dynamic type checking and recursive calls involved, especially in a deep copy. However, reflection provides a generic way to copy or clone maps, even when the exact types are not known at compile-time, offering a degree of flexibility and generality in handling various data structures.
5. Custom Implementation Based on Type
Creating a custom implementation based on types allows for a more targeted approach in copying maps. It gives you the flexibility to define the copying process, specifically according to the types and structures you are dealing with.
Creating a shallow copy of a map based on specific types involves duplicating the outer map while keeping references to the same inner elements.
Creating a deep copy of a map based on specific types involves duplicating not only the outer map but also the inner elements, ensuring no shared references.
In these examples, shallowCopyIntMap and deepCopyStructMap functions are designed specifically based on the data types in the maps.
Frequently Asked Questions (FAQs)
What does it mean to "copy a map" in Go?
Copying a map in Go refers to creating a new map that contains all the keys and values of the original map. Depending on how you choose to copy the map, the new map might share references to the same underlying data (shallow copy) or have completely separate data (deep copy).
What is the difference between a "shallow copy" and a "deep copy" of a map in Go?
A shallow copy of a map involves creating a new map with new keys, but the values still hold references to the same objects as the original map. In a deep copy, a new map is created with new keys, and new objects are also created for the values, ensuring no shared references with the original map.
How do you perform a shallow copy of a map in Go?
A shallow copy can be performed using a simple loop to copy keys and values from the original map to a new one, for instance using the range loop. The new map gets filled with the keys and values of the original map, but the values (if they are reference types like slices or maps) still point to the same memory locations.
How do you perform a deep copy of a map in Go?
A deep copy can be achieved using various methods such as manually creating new objects for each value, utilizing the encoding/json package to marshal and unmarshal the map, or using third-party libraries that offer deep copying functionalities like copier .
When should I use a shallow copy, and when should I use a deep copy?
Use a shallow copy when it’s acceptable to have the copied map's values point to the same references as the original map’s values. Choose a deep copy when you want the copied map to be entirely independent of the original map, with no shared references, ensuring changes in the copied map do not affect the original map.
Is it possible to copy maps of custom structs in Go?
Yes, you can copy maps with custom structs as values. When copying, you can choose whether to perform a shallow copy, where the struct references are shared, or a deep copy, where new struct instances are created for the copied map.
Are there any third-party libraries that help in copying maps in Go?
Yes, there are third-party libraries, like copier or go-cmp , which provide functionalities to easily perform shallow and deep copies of maps and other data structures in Go. Before using a third-party library, ensure it is well-maintained and widely accepted by the community for reliability and performance.
Does Go have built-in support for deep copying maps?
Go doesn’t have a built-in function specifically for deep copying maps. However, you can achieve deep copying by using various techniques such as the encoding/json package for marshaling and unmarshaling maps, creating custom functions, or using third-party libraries.
In conclusion, copying or cloning maps in Go can be accomplished through various methods, each with its unique applications and considerations. The fundamental distinction lies between performing a shallow copy and a deep copy. A shallow copy is simpler and entails replicating the map's structure and entries, but not deeply duplicating nested reference types, leading to shared references. In contrast, a deep copy creates a completely independent copy of the map, ensuring that all nested elements, even those of reference types, are independently replicated without sharing memory references.
Different techniques, ranging from using simple range loops, JSON marshaling and unmarshaling, reflection, custom type-based implementations, to employing third-party packages, offer a spectrum of tools to achieve the desired level of copying. The selection of a suitable method is guided by various factors such as the necessity to maintain or sever reference linkages between the original and copied maps, performance considerations, and the specific data types and structures involved.
For more information and official documentation on maps in Go, refer to the following links:
- Go Maps in Action - A blog post that goes deep into maps, providing a fundamental understanding.
- Go by Example: Maps - An illustrative guide that walks through the basics of using maps in Go.
- Go Documentation: Map Type - The official Go documentation describing the map type.
Tuan Nguyen
He is proficient in Golang, Python, Java, MongoDB, Selenium, Spring Boot, Kubernetes, Scrapy, API development, Docker, Data Scraping, PrimeFaces, Linux, Data Structures, and Data Mining. With expertise spanning these technologies, he develops robust solutions and implements efficient data processing and management strategies across various projects and platforms. 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.
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.
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
- Stack Overflow for Teams Where developers & technologists share private knowledge with coworkers
- Advertising & Talent Reach devs & technologists worldwide about your product, service or employer brand
- OverflowAI GenAI features for Teams
- OverflowAPI Train & fine-tune LLMs
- Labs The future of collective knowledge sharing
- About the company Visit the blog
Collectives™ on Stack Overflow
Find centralized, trusted content and collaborate around the technologies you use most.
Q&A for work
Connect and share knowledge within a single location that is structured and easy to search.
Get early access and see previews of new features.
Does Golang Copy the String on Modification/Write?
After reading the answer on Does Go language use Copy-on-write for strings , I feel the question was not adequately answered.
Given the example below, what is actually happening under the hood?
The question is when will golang determine there is a need to create a new copy?
- 3 strings are immutable in Go, so there is no "modification" of string values in go. You can change the value stored in a variable but that's not the same. – mkopriva Commented Aug 12, 2019 at 9:20
- @mkopriva, I understand that is is immutable other than append operation. Please refer to the code, both s and t are sharing the same underlying data at the beginning, but later on, t will become a separate copy, so is it true that Go copies the string when u attempt to modify the underlying data? – Mox Commented Aug 12, 2019 at 9:23
- 1 @mkopriva, that is not true, when we assign s to t, they are in fact pointing to the same address which holds the string data. – Mox Commented Aug 12, 2019 at 9:28
- 1 @Mox you're right, my bad. The two variables do indeed point to the same data. However, it still stands, that any kind of attempt at "modification" of that data (e.g. a += b , etc.) creates a new string, at a new place in memory. play.golang.com/p/tiJX6BL-ogJ – mkopriva Commented Aug 12, 2019 at 9:57
- 1 @Mox but how can you modify something that's immutable? Are you specifically looking for the implementation of += when the operands are strings? – mkopriva Commented Aug 12, 2019 at 10:06
2 Answers 2
In Go, string values are read-only byte slices and you cannot change its elements (immutable). Since it is a slice, it means that it has a backing (underlaying) array that has defined capacity. This being said, we can say that string is a data structure that points to a read-only backing array.
Strings are optimized for high reusability and thus read-only. Whenever you modify a string a new string (byte slice) is created in the background which makes it a bit of costly operation. One recommendation is to convert a string to an actual byte slice []byte(string) and work with bytes or use strings.Builder when your program needs to do a lot of string manipulations.
StringHeader - data structure
String Data Type in GO
- therefore Golang implements Copy on write/modification on string, m i rite? – Mox Commented Aug 12, 2019 at 9:34
- 1 @Mox: no, this term means something else. You don't "write" to an existing string here. You explicitly create a new one. – Sergio Tulentsev Commented Aug 12, 2019 at 9:38
- 1 Yes, copies the old value and creates new with the modifications made 👍 – Vane Trajkov Commented Aug 12, 2019 at 9:38
- 1 @mox no. COW involves modifiable shared resources. You don’t have those here. – Sergio Tulentsev Commented Aug 12, 2019 at 9:42
- 1 @Mox: Yes, let's it read it, shall we? The very first sentence of the chapter says “A string is an immutable sequence of bytes.” And because it's immutable, there are no writes. Hence "copy on write " is not applicable. If you want new strings, you have to create them. Not some background COW magic. – Sergio Tulentsev Commented Aug 12, 2019 at 10:05
After much debate/discussion on this at the comment sections, here is my conclusion.
Golang does not have copy-on-write.
The += here is an explicitly creating a new string which is equivalent to s = s + "World" which creates a new string and assign it back to s
And if you try to write the following code, it will result in compilation error due to immutability of the Golang string
As a result, everything in Golang is explicit, nothing is done implicitly by Golang. That is why copy-on-write does not exist in Golang.
Note: COW and immutability are not mutually exclusive.
Your Answer
Reminder: Answers generated by artificial intelligence tools are not allowed on Stack Overflow. Learn more
Sign up or log in
Post as a guest.
Required, but never shown
By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy .
Not the answer you're looking for? Browse other questions tagged go or ask your own question .
- The Overflow Blog
- Where does Postgres fit in a world of GenAI and vector databases?
- Mobile Observability: monitoring performance through cracked screens, old...
- Featured on Meta
- We've made changes to our Terms of Service & Privacy Policy - July 2024
- Bringing clarity to status tag usage on meta sites
- What does a new user need in a homepage experience on Stack Overflow?
- Feedback requested: How do you use tag hover descriptions for curating and do...
- Staging Ground Reviewer Motivation
Hot Network Questions
- How do we reconcile the story of the woman caught in adultery in John 8 and the man stoned for picking up sticks on Sabbath in Numbers 15?
- The answer is not wrong
- Is there a phrase for someone who's really bad at cooking?
- Has a tire ever exploded inside the Wheel Well?
- Maximizing the common value of both sides of an equation
- Why did the Fallschirmjäger have such terrible parachutes?
- Background for the Elkies-Klagsbrun curve of rank 29
- Book or novel about an intelligent monolith from space that crashes into a mountain
- How much of a discount do you get when buying cards on sale?
- How can judicial independence be jeopardised by politicians' criticism?
- I overstayed 90 days in Switzerland. I have EU residency and never got any stamps in passport. Can I exit/enter at airport without trouble?
- My visit is for two weeks but my host bought insurance for two months is it okay
- What is the difference between a "Complaint for Civil Protection Order" and a "Motion for Civil Protection Order"?
- I'm trying to remember a novel about an asteroid threatening to destroy the earth. I remember seeing the phrase "SHIVA IS COMING" on the cover
- If inflation/cost of living is such a complex difficult problem, then why has the price of drugs been absoultly perfectly stable my whole life?
- How could I contact the Betriebsrat (Workers' Union) of my employer behind his back?
- Fast matrix multiplication for large matrices in Python
- Is the spectrum of Hawking radiation identical to that of thermal radiation?
- Is it possible to accurately describe something without describing the rest of the universe?
- Where does the energy in ion propulsion come from?
- What happens if all nine Supreme Justices recuse themselves?
- Why is the movie titled "Sweet Smell of Success"?
- How is it possible to know a proposed perpetual motion machine won't work without even looking at it?
- Why is "on" the optimal preposition in "tilt on its axis"?
IMAGES
VIDEO
COMMENTS
String in go are immutable once created. Go specs. I will prefer builder as below. You go on adding to buffer of builder (mutably) WriteString and once done call String method which is returning pointer and not another copy of the buffer slice. somestring := "Hello Go" var sb strings.Builder if _, err := sb.WriteString(somestring); err != nil { //log & return } newstring := sb.String()
0. While learning nuances of array data structure in Go I came across an interesting confusion. I have learnt from blog.golang.org that -. when you assign or pass around an array value you will make a copy of its contents. To check this myself I wrote the following piece of code: x := []int{2, 4, 5} y := x.
Copy a slice using the append() function. Output: source slice: [a b c], address: 0xc0000981b0. Copying a slice using the append() function is really simple. You just need to define a new empty slice, and use the append() to add all elements of the src to the dst slice. In that way, you get a new slice with all the elements duplicated.
The copy() function in Go requires two arguments: the destination slice first and the source slice second, with both being of the same type. It returns the count of elements copied. You can disregard this return value if it's not needed. A key aspect of copy() is that the lengths of the destination and source slices can differ; it copies elements only up to the length of the shorter slice.
func copy(dst, src []Type) int It returns the number of elements copied, which will be the minimum of len(dst) and len(src). The result does not depend on whether the arguments overlap. As a special case, it's legal to copy bytes from a string to a slice of bytes. copy(dst []byte, src string) int Examples Copy from one slice to another
If there is enough space in the array of the original slice, append will take two steps: Copy each appended value in order into the available elements in the backing array. Return a new slice backed by the array of the original slice. In the following code we append one value to fruits and assign the results to food.
copy built-in function. copy function copies elements from a source (src) slice into a destination (dst) slice. func copy(dst, src []Type) int. Create a new empty slice with the same size of the src and then copy all the elements of the src to the empty slice. Using the copy function, src and dst slices have different backing arrays.
Arrays. 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 ...
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.
In Slice, you can copy one slice into another slice using the copy () function provided by the Go language. Or in other words, copy () function allows you to copy the elements of one slice into another slice. Syntax: Here, dst represents the destination slice and src represents the source slice. It will return the number of elements copied that ...
Golang | copy() Function: Here, we are going to learn about the built-in copy() function with its usages, syntax, and examples. Submitted by IncludeHelp, on October 14, 2021 [Last updated : March 15, 2023] . copy() Function. In the Go programming language, the copy() is a built-in function that is used to copy the elements from a source slice into a destination slice and returns the number of ...
That is: An integer, a string and an array will be deep copied when a struct or a map is copied. A slice will be shallow copied when a struct or a map is copied. Consider the following code for an example ( link to playground ): package main import (. "fmt" "log" "strings" ) type myStruct struct {.
Copying an array by value creates a new copy of the array, whereas copying by reference creates a new reference to the same underlying array. Additionally, developers should also be familiar with how to copy parts of an array into another array using the built-in copy () function. With these techniques, developers can effectively work with ...
Use the Join function in the strings standard package. The Buffer type in the bytes standard package (or the built-in copy function) can be used to build byte slices, which afterwards can be converted to string values. Since Go 1.10, the Builder type in the strings standard package can be used to build strings.
This would result in two different objects referring to the same slice object. // initialize a struct. t := T{I: 1, S: "one", xs: []int{1, 2, 3}} // make struct copy. u := t // u has its field values equal to t. Since both u and t refer to the same slice through their field xs updating a value in the slice of one object would reflect the change ...
Here we make a slice of strings of length 3 (initially zero-valued). By default a new slice's capacity is equal to its length; ... Slices can also be copy'd. Here we create an empty slice c of the same length as s and copy into c from s. c:= make ([] string, len (s)) copy (c, s) fmt.
As in all languages in the C family, everything in Go is passed by value. That is, a function always gets a copy of the thing being passed, as if there were an assignment statement assigning the value to the parameter. For instance, passing an int value to a function makes a copy of the int, and passing a pointer value makes a copy of the ...
In go, primitive types, and structs containing only primitive types, are copied by value, so you can copy them by simply assigning to a new variable (or returning from a function). For example: type Person struct{. Name string. Age int. } alice1 := Person{"Alice", 30} alice2 := alice1.
Different Methods to Copy Map in GO. Here are various methods you can use to copy or clone a map in Go (Golang): Using Range Loop with Make: A simple method where a new map is initialized, and elements from the original map are iterated over and assigned to the new map, ensuring a separate memory allocation.; Using JSON Marshalling and Unmarshalling: This technique involves converting the ...
1. After much debate/discussion on this at the comment sections, here is my conclusion. Golang does not have copy-on-write. The += here is an explicitly creating a new string which is equivalent to s = s + "World" which creates a new string and assign it back to s.