Unpack slices on assignment?

Go

Go Problem Overview


Is there an elegant way in Go to do multiple assignments from arrays like in Python? Here is a Python example of what I'm trying to do (split a string and then assign the resulting array into two variables).

python:
>>> a, b = "foo;bar".split(";")

My current solution is:

x := strings.Split("foo;bar", ";")
a, b := x[0], x[1]

I'm can see this getting messy in some constructs. The practical example I'm currently facing is a bookmark file parsing and assigning to a map:

bookmark := make(map[string]string)
x := strings.Split("foo\thttps://bar", "\t")
name, link := x[0], x[1]
bookmark[name] = link

Now I have a useless variable x sitting around. I'd like to do something like:

bookmark := make(map[string]string)
name, line := strings.Split("foo\thttps://bar", "\t")
bookmark[name] = link

but that's invalid.

Go Solutions


Solution 1 - Go

As Sergio Tulentsev mentioned, general packing/unpacking as is done in Python is not supported. I think the way to go there is to define your own small ad-hoc function using multiple return values:

func splitLink(s, sep string) (string, string) {
    x := strings.Split(s, sep)
    return x[0], x[1]
}

And you can then write:

name, link := splitLink("foo\thttps://bar", "\t")

But this will obviously work only when at least two substrings are being split, and silently ignore if more than two were. If this is something you use a lot, it might make your code more readable though.

--EDIT--

Another way to unpack an array is via variadic pointer arguments:

func unpack(s []string, vars... *string) {
    for i, str := range s {
        *vars[i] = str
    }
}

Which let you write:

var name, link string
unpack(strings.Split("foo\thttps://bar", "\t"), &name, &link)
bookmarks[name] = link

This will work for any array size, but it is arguably less readable, and you have to declare your variables explicitly.

Solution 2 - Go

If your function is meant to split a string only by the first occurrence of the separator, you can always make your own function:

package main

import (
	"fmt"
	"strings"
)

func Split(s, sep string) (string, string) {
	// Empty string should just return empty
	if len(s) == 0 {
		return s, s
	}	
	
	slice := strings.SplitN(s, sep, 2)
	
	// Incase no separator was present
	if len(slice) == 1 {
		return slice[0], ""
	}
		
	return slice[0], slice[1]
}

func main() {
	a, b := Split("foo;bar;foo", ";")
	fmt.Println(a, b)
}

Output:

>foo bar;foo

Playground

Solution 3 - Go

You could also use anonymous functions:

a, b := func() (string, string) {
    x := strings.Split("foo;bar", ";")
    return x[0], x[1]
}()

Note: don't forget the () on the end of the closing bracket } otherwise you will get the error:

assignment mismatch: 2 variable but 1 values

This is because without the () a function (1 value) is returned not the expected strings (2 values).

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSebastian BartosView Question on Stackoverflow
Solution 1 - GovalView Answer on Stackoverflow
Solution 2 - GoANisusView Answer on Stackoverflow
Solution 3 - GoJames BurkeView Answer on Stackoverflow