Jak efektywnie łączyć struny w Go?

W Go, string jest typem prymitywnym, co oznacza, że jest tylko do odczytu, a każda manipulacja nim utworzy nowy ciąg znaków.

Więc jeśli chcę łączyć ciągi wiele razy, nie znając długości wynikowego ciągu, jaki jest najlepszy sposób, aby to zrobić?

Naiwnym sposobem byłoby:

s := ""
for i := 0; i < 1000; i++ {
    s += getShortStringFromSomewhere()
}
return s
Ale to nie wydaje się zbyt efektywne.
Author: jlucktay, 2009-11-19

20 answers

Najlepszym sposobem jest użycie bytes paczka. Posiada Buffer typ, który realizuje io.Writer.

package main

import (
    "bytes"
    "fmt"
)

func main() {
    var buffer bytes.Buffer

    for i := 0; i < 1000; i++ {
        buffer.WriteString("a")
    }

    fmt.Println(buffer.String())
}
To robi to w O (n) czasie.

Uwaga dodana w 2018

Od Go 1.10 są ciągi.Typ Builder , który osiąga to jeszcze efektywniej (dla ciągów). Podany tam przykład jest zwięzły i łatwy do skopiowania/dostosowania.

Jest to analogiczne do klasyStringBuilder w Javie.

 722
Author: marketer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-01 14:11:27

Najskuteczniejszym sposobem łączenia łańcuchów jest użycie wbudowanej funkcji copy. W moich testach to podejście jest ~3x szybsze niż przy użyciu bytes.Buffer i znacznie szybciej (~12 000 x) niż używając operatora +. Ponadto zużywa mniej pamięci.

Stworzyłem przypadek testowy aby to udowodnić, a oto wyniki:

BenchmarkConcat  1000000    64497 ns/op   502018 B/op   0 allocs/op
BenchmarkBuffer  100000000  15.5  ns/op   2 B/op        0 allocs/op
BenchmarkCopy    500000000  5.39  ns/op   0 B/op        0 allocs/op

Poniżej znajduje się kod do testów:

package main

import (
    "bytes"
    "strings"
    "testing"
)

func BenchmarkConcat(b *testing.B) {
    var str string
    for n := 0; n < b.N; n++ {
        str += "x"
    }
    b.StopTimer()

    if s := strings.Repeat("x", b.N); str != s {
        b.Errorf("unexpected result; got=%s, want=%s", str, s)
    }
}

func BenchmarkBuffer(b *testing.B) {
    var buffer bytes.Buffer
    for n := 0; n < b.N; n++ {
        buffer.WriteString("x")
    }
    b.StopTimer()

    if s := strings.Repeat("x", b.N); buffer.String() != s {
        b.Errorf("unexpected result; got=%s, want=%s", buffer.String(), s)
    }
}

func BenchmarkCopy(b *testing.B) {
    bs := make([]byte, b.N)
    bl := 0

    b.ResetTimer()
    for n := 0; n < b.N; n++ {
        bl += copy(bs[bl:], "x")
    }
    b.StopTimer()

    if s := strings.Repeat("x", b.N); string(bs) != s {
        b.Errorf("unexpected result; got=%s, want=%s", string(bs), s)
    }
}

// Go 1.10
func BenchmarkStringBuilder(b *testing.B) {
    var strBuilder strings.Builder

    b.ResetTimer()
    for n := 0; n < b.N; n++ {
        strBuilder.WriteString("x")
    }
    b.StopTimer()

    if s := strings.Repeat("x", b.N); strBuilder.String() != s {
        b.Errorf("unexpected result; got=%s, want=%s", strBuilder.String(), s)
    }
}
 245
Author: cd1,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-13 00:33:24

W pakiecie łańcuchów istnieje funkcja biblioteczna o nazwie Join: http://golang.org/pkg/strings/#Join

Spojrzenie na kod Join Pokazuje podobne podejście do dodawania funkcji Kinopiko napisał (a): https://golang.org/src/strings/strings.go#L420

Użycie:

import (
    "fmt";
    "strings";
)

func main() {
    s := []string{"this", "is", "a", "joined", "string\n"};
    fmt.Printf(strings.Join(s, " "));
}

$ ./test.bin
this is a joined string
 119
Author: mbarkhau,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-28 08:24:36

Począwszy od Go 1.10 jest strings.Builder, tutaj .

Konstruktor jest używany do efektywnego budowania ciągu znaków za pomocą metod zapisu. Minimalizuje kopiowanie pamięci. Wartość zero jest gotowa do użycia.


Użycie:

To prawie to samo z bytes.Buffer.

package main

import (
    "strings"
    "fmt"
)

func main() {
    var str strings.Builder

    for i := 0; i < 1000; i++ {
        str.WriteString("a")
    }

    fmt.Println(str.String())
}

Metody StringBuilder i interfejsy, które obsługuje:

Jego metody są implementowane z myślą o istniejących interfejsach, dzięki czemu można można łatwo przełączyć się na nowy Konstruktor w kodzie.


Użycie wartości zerowej:

var sb strings.Builder

Różnice od bajtów.Bufor:

  • Jest niezmienny i może tylko rosnąć lub zresetować.

  • W bytes.Buffer bajtach bazowych można escape like this: (*Buffer).Bytes(); strings.Builder zapobiega temu problemowi.

  • Posiada również mechanizm copyCheck wewnątrz, który zapobiega przypadkowemu skopiowaniu go (func (b *Builder) copyCheck() { ... }).


Sprawdź jego kod źródłowy tutaj.

 60
Author: Inanc Gumus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-01 12:22:47

Właśnie porównałem górną odpowiedź zamieszczoną powyżej w moim własnym kodzie (rekurencyjny spacer po drzewie) i prosty operator concat jest w rzeczywistości szybszy niż BufferString.

func (r *record) String() string {
    buffer := bytes.NewBufferString("");
    fmt.Fprint(buffer,"(",r.name,"[")
    for i := 0; i < len(r.subs); i++ {
        fmt.Fprint(buffer,"\t",r.subs[i])
    }
    fmt.Fprint(buffer,"]",r.size,")\n")
    return buffer.String()
}

Trwało to 0,81 s, natomiast następujący kod:

func (r *record) String() string {
    s := "(\"" + r.name + "\" ["
    for i := 0; i < len(r.subs); i++ {
        s += r.subs[i].String()
    }
    s += "] " + strconv.FormatInt(r.size,10) + ")\n"
    return s
} 

Zajęło tylko 0,61 s. jest to prawdopodobnie spowodowane kosztami tworzenia nowych buforów.

Update: porównałem również funkcję join i działała w 0.54 s

func (r *record) String() string {
    var parts []string
    parts = append(parts, "(\"", r.name, "\" [" )
    for i := 0; i < len(r.subs); i++ {
        parts = append(parts, r.subs[i].String())
    }
    parts = append(parts, strconv.FormatInt(r.size,10), ")\n")
    return strings.Join(parts,"")
}
 37
Author: JasonMc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-29 02:02:43

Jest to najszybsze rozwiązanie, które nie wymaga najpierw musisz poznać lub obliczyć całkowity rozmiar bufora:

var data []byte
for i := 0; i < 1000; i++ {
    data = append(data, getShortStringFromSomewhere()...)
}
return string(data)

Według mojegobenchmark , jest 20% wolniejszy niż rozwiązanie kopiujące (8,1 ns na append zamiast 6.72 ns), ale nadal o 55% szybciej niż przy użyciu bajtów.Bufor.

 19
Author: rog,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-08 13:13:56

Można utworzyć duży kawałek bajtów i skopiować do niego bajty krótkich łańcuchów za pomocą plasterków. Istnieje funkcja podana w "efektywnym Go":

func Append(slice, data[]byte) []byte {
    l := len(slice);
    if l + len(data) > cap(slice) { // reallocate
        // Allocate double what's needed, for future growth.
        newSlice := make([]byte, (l+len(data))*2);
        // Copy data (could use bytes.Copy()).
        for i, c := range slice {
            newSlice[i] = c
        }
        slice = newSlice;
    }
    slice = slice[0:l+len(data)];
    for i, c := range data {
        slice[l+i] = c
    }
    return slice;
}

Następnie po zakończeniu operacji użyj string ( ) na dużym kawałku bajtów, aby ponownie przekształcić go w ciąg znaków.

 18
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-19 03:57:38

Aktualizacja 2018-04-03

Od teraz 1.10, string.Builder zaleca się zastąpienie bytes.Buffer. sprawdź informacje o wydaniu 1.10

Nowy konstruktor typów jest zamiennikiem bajtów.Bufor dla przypadku użycia nagromadzenia tekstu w wyniku ciągu znaków. API Konstruktora jest ograniczonym podzbiorem bajtów.Bufora, który pozwala bezpiecznie uniknąć powielania danych podczas ciągu metoda.

============================================================

Kod odniesienia @cd1 i inne odpowiedzi są błędne. b.N nie powinien być ustawiony w funkcji benchmark. Jest ustawiany dynamicznie przez narzędzie testowe go, aby określić, czy czas wykonania testu jest stabilny.

Funkcja benchmark powinna wykonywać ten sam test b.N razy, a test wewnątrz pętli powinien być taki sam dla każdej iteracji. Więc naprawiam to dodając wewnętrzną pętlę. Dodam również benchmarki dla niektórych innych rozwiązań:

package main

import (
    "bytes"
    "strings"
    "testing"
)

const (
    sss = "xfoasneobfasieongasbg"
    cnt = 10000
)

var (
    bbb      = []byte(sss)
    expected = strings.Repeat(sss, cnt)
)

func BenchmarkCopyPreAllocate(b *testing.B) {
    var result string
    for n := 0; n < b.N; n++ {
        bs := make([]byte, cnt*len(sss))
        bl := 0
        for i := 0; i < cnt; i++ {
            bl += copy(bs[bl:], sss)
        }
        result = string(bs)
    }
    b.StopTimer()
    if result != expected {
        b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
    }
}

func BenchmarkAppendPreAllocate(b *testing.B) {
    var result string
    for n := 0; n < b.N; n++ {
        data := make([]byte, 0, cnt*len(sss))
        for i := 0; i < cnt; i++ {
            data = append(data, sss...)
        }
        result = string(data)
    }
    b.StopTimer()
    if result != expected {
        b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
    }
}

func BenchmarkBufferPreAllocate(b *testing.B) {
    var result string
    for n := 0; n < b.N; n++ {
        buf := bytes.NewBuffer(make([]byte, 0, cnt*len(sss)))
        for i := 0; i < cnt; i++ {
            buf.WriteString(sss)
        }
        result = buf.String()
    }
    b.StopTimer()
    if result != expected {
        b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
    }
}

func BenchmarkCopy(b *testing.B) {
    var result string
    for n := 0; n < b.N; n++ {
        data := make([]byte, 0, 64) // same size as bootstrap array of bytes.Buffer
        for i := 0; i < cnt; i++ {
            off := len(data)
            if off+len(sss) > cap(data) {
                temp := make([]byte, 2*cap(data)+len(sss))
                copy(temp, data)
                data = temp
            }
            data = data[0 : off+len(sss)]
            copy(data[off:], sss)
        }
        result = string(data)
    }
    b.StopTimer()
    if result != expected {
        b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
    }
}

func BenchmarkAppend(b *testing.B) {
    var result string
    for n := 0; n < b.N; n++ {
        data := make([]byte, 0, 64)
        for i := 0; i < cnt; i++ {
            data = append(data, sss...)
        }
        result = string(data)
    }
    b.StopTimer()
    if result != expected {
        b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
    }
}

func BenchmarkBufferWrite(b *testing.B) {
    var result string
    for n := 0; n < b.N; n++ {
        var buf bytes.Buffer
        for i := 0; i < cnt; i++ {
            buf.Write(bbb)
        }
        result = buf.String()
    }
    b.StopTimer()
    if result != expected {
        b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
    }
}

func BenchmarkBufferWriteString(b *testing.B) {
    var result string
    for n := 0; n < b.N; n++ {
        var buf bytes.Buffer
        for i := 0; i < cnt; i++ {
            buf.WriteString(sss)
        }
        result = buf.String()
    }
    b.StopTimer()
    if result != expected {
        b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
    }
}

func BenchmarkConcat(b *testing.B) {
    var result string
    for n := 0; n < b.N; n++ {
        var str string
        for i := 0; i < cnt; i++ {
            str += sss
        }
        result = str
    }
    b.StopTimer()
    if result != expected {
        b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
    }
}

Środowisko to OS X 10.11.6, 2.2 GHz Intel Core i7

Wyniki testu:

BenchmarkCopyPreAllocate-8         20000             84208 ns/op          425984 B/op          2 allocs/op
BenchmarkAppendPreAllocate-8       10000            102859 ns/op          425984 B/op          2 allocs/op
BenchmarkBufferPreAllocate-8       10000            166407 ns/op          426096 B/op          3 allocs/op
BenchmarkCopy-8                    10000            160923 ns/op          933152 B/op         13 allocs/op
BenchmarkAppend-8                  10000            175508 ns/op         1332096 B/op         24 allocs/op
BenchmarkBufferWrite-8             10000            239886 ns/op          933266 B/op         14 allocs/op
BenchmarkBufferWriteString-8       10000            236432 ns/op          933266 B/op         14 allocs/op
BenchmarkConcat-8                     10         105603419 ns/op        1086685168 B/op    10000 allocs/op

Wniosek:

  1. CopyPreAllocate jest najszybszym sposobem; AppendPreAllocate jest dość blisko numeru 1, ale łatwiej jest napisać kod.
  2. Concat ma naprawdę złą wydajność zarówno pod względem szybkości, jak i wykorzystania pamięci. Nie używaj go.
  3. Buffer#Write i Buffer#WriteString są w zasadzie takie same w szybkości, wbrew temu, co powiedział @Dani-Br w komentarzu. Rozważając string jest rzeczywiście []byte W Go, to ma sens.
  4. bajtów.Bufor zasadniczo używa tego samego rozwiązania, co Copy z dodatkową księgowością i innymi rzeczami.
  5. Copy i Append używają rozmiaru bootstrap 64, takiego samego jak bajty.Bufor
  6. Append użyj więcej pamięci i allocs, myślę, że jest to związane z algorytmem wzrostu, którego używa. Nie rośnie pamięć tak szybko jak bajty.Bufor

Sugestia:

  1. do prostych zadań, takich jak to, czego chce OP, użyłbym Append lub AppendPreAllocate. Jest wystarczająco szybki i łatwy w użyciu.
  2. Jeśli chcesz jednocześnie odczytywać i zapisywać bufor, użyj oczywiście bytes.Buffer. Do tego jest zaprojektowany.
 17
Author: PickBoy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-03 17:26:12
package main

import (
  "fmt"
)

func main() {
    var str1 = "string1"
    var str2 = "string2"
    out := fmt.Sprintf("%s %s ",str1, str2)
    fmt.Println(out)
}
 15
Author: harold ramos,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-06 20:55:48

Moja pierwotna sugestia brzmiała

s12 := fmt.Sprint(s1,s2)

Ale powyżej odpowiedz używając bajtów.Bufor-WriteString () jest najbardziej efektywnym sposobem.

Moja początkowa sugestia wykorzystuje odbicie i przełącznik typu. Zobacz (p *pp) doPrint i (p *pp) printArg
Nie ma uniwersalnego interfejsu Stringer () dla podstawowych typów, jak naiwnie myślałem.

Przynajmniej Sprint () wewnętrznie używa bajtów.Bufor. Tak więc

`s12 := fmt.Sprint(s1,s2,s3,s4,...,s1000)`

Jest akceptowalny pod względem alokacji pamięci.

=> Konkatenacja Sprint() może być używana do szybkiego debugowania.
= > W przeciwnym razie użyj bajtów.Bufor ... WriteString

 12
Author: Peter Buchmann,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-11 10:50:11

Rozszerzanie odpowiedzi cd1: Możesz użyć append () zamiast copy (). append() tworzy coraz większe rezerwy z wyprzedzeniem, kosztując trochę więcej pamięci, ale oszczędzając czas. Dodałem jeszcze dwa benchmarki na górze twojego. Uruchom lokalnie z

go test -bench=. -benchtime=100ms

Na moim thinkpad T400s daje:

BenchmarkAppendEmpty    50000000         5.0 ns/op
BenchmarkAppendPrealloc 50000000         3.5 ns/op
BenchmarkCopy           20000000        10.2 ns/op
 10
Author: Peter Buchmann,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-25 02:40:45

To jest aktualna wersja benchmarka dostarczona przez @ cd1 (Go 1.8, linux x86_64) z poprawkami błędów wymienionych przez @icza i @PickBoy.

Bytes.Buffer jest tylko 7 razy szybsza niż bezpośrednia konkatenacja ciągu za pomocą operatora +.

package performance_test

import (
    "bytes"
    "fmt"
    "testing"
)

const (
    concatSteps = 100
)

func BenchmarkConcat(b *testing.B) {
    for n := 0; n < b.N; n++ {
        var str string
        for i := 0; i < concatSteps; i++ {
            str += "x"
        }
    }
}

func BenchmarkBuffer(b *testing.B) {
    for n := 0; n < b.N; n++ {
        var buffer bytes.Buffer
        for i := 0; i < concatSteps; i++ {
            buffer.WriteString("x")
        }
    }
}

Timingi:

BenchmarkConcat-4                             300000          6869 ns/op
BenchmarkBuffer-4                            1000000          1186 ns/op
 1
Author: Vitaly Isaev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-02 11:06:51

Dla tych, którzy pochodzą ze świata Javy, gdzie mamy StringBuilder do efektywnej konkatenacji łańcuchów, wydaje się, że najnowsza wersja go ma swój odpowiednik i nazywa się Builder: https://github.com/golang/go/blob/master/src/strings/builder.go

 0
Author: Joel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-22 23:35:02

Robię to używając: -

package main

import (
    "fmt"
    "strings"
)

func main (){
    concatenation:= strings.Join([]string{"a","b","c"},"") //where second parameter is a separator. 
    fmt.Println(concatenation) //abc
}
 0
Author: Krish Bhanushali,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-26 00:52:48
package main

import (
"fmt"
)

func main() {
    var str1 = "string1"
    var str2 = "string2"
    result := make([]byte, 0)
    result = append(result, []byte(str1)...)
    result = append(result, []byte(str2)...)
    result = append(result, []byte(str1)...)
    result = append(result, []byte(str2)...)

    fmt.Println(string(result))
}
 0
Author: rajni kant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-09 09:36:39

Wynik testu porównawczego ze statystykami alokacji pamięci. sprawdź kod benchmarka na github .

Użyj sznurków.Kreator optymalizacji wydajności.

go test -bench . -benchmem
goos: darwin
goarch: amd64
pkg: github.com/hechen0/goexp/exps
BenchmarkConcat-8                1000000             60213 ns/op          503992 B/op          1 allocs/op
BenchmarkBuffer-8               100000000               11.3 ns/op             2 B/op          0 allocs/op
BenchmarkCopy-8                 300000000                4.76 ns/op            0 B/op          0 allocs/op
BenchmarkStringBuilder-8        1000000000               4.14 ns/op            6 B/op          0 allocs/op
PASS
ok      github.com/hechen0/goexp/exps   70.071s
 0
Author: hechen0,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-05 16:30:42

Goutils.JoinBetween

 func JoinBetween(in []string, separator string, startIndex, endIndex int) string {
    if in == nil {
        return ""
    }

    noOfItems := endIndex - startIndex

    if noOfItems <= 0 {
        return EMPTY
    }

    var builder strings.Builder

    for i := startIndex; i < endIndex; i++ {
        if i > startIndex {
            builder.WriteString(separator)
        }
        builder.WriteString(in[i])
    }
    return builder.String()
}
 0
Author: Xian Shu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-18 03:01:06

Spójrz na bibliotekę golanga strconv dającą dostęp do kilku funkcji AppendXX, umożliwiając nam łączenie łańcuchów z łańcuchami i innymi typami danych.

 -1
Author: Akash Mishra,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-28 19:17:03
s := fmt.Sprintf("%s%s", []byte(s1), []byte(s2))
 -2
Author: user2288856,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-04 09:34:09

strings.Join() z pakietu "strings"

Jeśli masz niedopasowanie typu(np. jeśli próbujesz połączyć int i string), wykonujesz RANDOMTYPE (rzecz, którą chcesz zmienić)

EX:

package main

import "strings"

var intEX = 0
var stringEX = "hello all you "
var stringEX2 = " people in here"

func main() {
    strings.Join(stringEX, string(intEX), stringEX2)
}
 -2
Author: liam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-29 09:25:58