Equivalent of Python string.format in Go?












17















In Python, you can do this:



"File {file} had error {error}".format(file=myfile, error=err)


or this:



"File %(file)s had error %(error)s" % {"file": myfile, "error": err}


In Go, the simplest option is:



fmt.Sprintf("File %s had error %s", myfile, err)


which doesn't let you swap the order of the parameters in the format string, which you need to do for I18N. Go does have the template package, which would require something like:



package main

import (
"bytes"
"text/template"
"os"
)

func main() {
type Params struct {
File string
Error string
}

var msg bytes.Buffer

params := &Params{
File: "abc",
Error: "def",
}

tmpl, _ := template.New("errmsg").Parse("File {{.File}} has error {{.Error}}")
tmpl.Execute(&msg, params)
msg.WriteTo(os.Stdout)
}


which seems like a long way to go for an error message. Is there a more reasonable option that allows me to give string parameters independent of order?










share|improve this question





























    17















    In Python, you can do this:



    "File {file} had error {error}".format(file=myfile, error=err)


    or this:



    "File %(file)s had error %(error)s" % {"file": myfile, "error": err}


    In Go, the simplest option is:



    fmt.Sprintf("File %s had error %s", myfile, err)


    which doesn't let you swap the order of the parameters in the format string, which you need to do for I18N. Go does have the template package, which would require something like:



    package main

    import (
    "bytes"
    "text/template"
    "os"
    )

    func main() {
    type Params struct {
    File string
    Error string
    }

    var msg bytes.Buffer

    params := &Params{
    File: "abc",
    Error: "def",
    }

    tmpl, _ := template.New("errmsg").Parse("File {{.File}} has error {{.Error}}")
    tmpl.Execute(&msg, params)
    msg.WriteTo(os.Stdout)
    }


    which seems like a long way to go for an error message. Is there a more reasonable option that allows me to give string parameters independent of order?










    share|improve this question



























      17












      17








      17


      7






      In Python, you can do this:



      "File {file} had error {error}".format(file=myfile, error=err)


      or this:



      "File %(file)s had error %(error)s" % {"file": myfile, "error": err}


      In Go, the simplest option is:



      fmt.Sprintf("File %s had error %s", myfile, err)


      which doesn't let you swap the order of the parameters in the format string, which you need to do for I18N. Go does have the template package, which would require something like:



      package main

      import (
      "bytes"
      "text/template"
      "os"
      )

      func main() {
      type Params struct {
      File string
      Error string
      }

      var msg bytes.Buffer

      params := &Params{
      File: "abc",
      Error: "def",
      }

      tmpl, _ := template.New("errmsg").Parse("File {{.File}} has error {{.Error}}")
      tmpl.Execute(&msg, params)
      msg.WriteTo(os.Stdout)
      }


      which seems like a long way to go for an error message. Is there a more reasonable option that allows me to give string parameters independent of order?










      share|improve this question
















      In Python, you can do this:



      "File {file} had error {error}".format(file=myfile, error=err)


      or this:



      "File %(file)s had error %(error)s" % {"file": myfile, "error": err}


      In Go, the simplest option is:



      fmt.Sprintf("File %s had error %s", myfile, err)


      which doesn't let you swap the order of the parameters in the format string, which you need to do for I18N. Go does have the template package, which would require something like:



      package main

      import (
      "bytes"
      "text/template"
      "os"
      )

      func main() {
      type Params struct {
      File string
      Error string
      }

      var msg bytes.Buffer

      params := &Params{
      File: "abc",
      Error: "def",
      }

      tmpl, _ := template.New("errmsg").Parse("File {{.File}} has error {{.Error}}")
      tmpl.Execute(&msg, params)
      msg.WriteTo(os.Stdout)
      }


      which seems like a long way to go for an error message. Is there a more reasonable option that allows me to give string parameters independent of order?







      python string go






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Oct 26 '18 at 9:05









      icza

      166k25330364




      166k25330364










      asked Nov 25 '16 at 19:10









      Scott DeerwesterScott Deerwester

      97111027




      97111027
























          4 Answers
          4






          active

          oldest

          votes


















          29














          With strings.Replacer



          Using strings.Replacer, implementing a formatter of your desire is very easy and compact.



          func main() {
          file, err := "/data/test.txt", "file not found"

          log("File {file} had error {error}", "{file}", file, "{error}", err)
          }

          func log(format string, args ...string) {
          r := strings.NewReplacer(args...)
          fmt.Println(r.Replace(format))
          }


          Output (try it on the Go Playground):



          File /data/test.txt had error file not found


          We can make it more pleasant to use by adding the brackets to the parameter names automatically in the log() function:



          func main() {
          file, err := "/data/test.txt", "file not found"

          log2("File {file} had error {error}", "file", file, "error", err)
          }

          func log2(format string, args ...string) {
          for i, v := range args {
          if i%2 == 0 {
          args[i] = "{" + v + "}"
          }
          }
          r := strings.NewReplacer(args...)
          fmt.Println(r.Replace(format))
          }


          Output (try it on the Go Playground):



          File /data/test.txt had error file not found


          Yes, you could say that this only accepts string parameter values. This is true. With a little more improvement, this won't be true:



          func main() {
          file, err := "/data/test.txt", 666

          log3("File {file} had error {error}", "file", file, "error", err)
          }

          func log3(format string, args ...interface{}) {
          args2 := make(string, len(args))
          for i, v := range args {
          if i%2 == 0 {
          args2[i] = fmt.Sprintf("{%v}", v)
          } else {
          args2[i] = fmt.Sprint(v)
          }
          }
          r := strings.NewReplacer(args2...)
          fmt.Println(r.Replace(format))
          }


          Output (try it on the Go Playground):



          File /data/test.txt had error 666


          A variant of this to accept params as a map[string]interface{} and return the result as a string:



          type P map[string]interface{}

          func main() {
          file, err := "/data/test.txt", 666

          s := log33("File {file} had error {error}", P{"file": file, "error": err})
          fmt.Println(s)
          }

          func log33(format string, p P) string {
          args, i := make(string, len(p)*2), 0
          for k, v := range p {
          args[i] = "{" + k + "}"
          args[i+1] = fmt.Sprint(v)
          i += 2
          }
          return strings.NewReplacer(args...).Replace(format)
          }


          Try it on the Go Playground.



          With text/template



          Your template solution or proposal is also way too verbose. It can be written as compact as this (error checks omitted):



          type P map[string]interface{}

          func main() {
          file, err := "/data/test.txt", 666

          log4("File {{.file}} has error {{.error}}", P{"file": file, "error": err})
          }

          func log4(format string, p P) {
          t := template.Must(template.New("").Parse(format))
          t.Execute(os.Stdout, p)
          }


          Output (try it on the Go Playground):



          File /data/test.txt has error 666


          If you want to return the string (instead of printing it to the standard output), you may do it like this (try it on the Go Playground):



          func log5(format string, p P) string {
          b := &bytes.Buffer{}
          template.Must(template.New("").Parse(format)).Execute(b, p)
          return b.String()
          }


          Using explicit argument indices



          This was already mentioned in another answer, but to complete it, know that the same explicit argument index may be used arbitrary number of times and thus resulting in the same parameter substituted in multiple times. Read more about this in this question: Replace all variables in Sprintf with same variable






          share|improve this answer


























          • I suppose that you could also combine the approach in my answer using a map, below, with your approach in log3, putting the args into a map instead of a string array and then formatting it as you do with log4. A lot of the complication in my example was to achieve returning a string. Could you modify log4 to accommodate that?

            – Scott Deerwester
            Nov 25 '16 at 20:30











          • @ScottDeerwester Yes, added a log33() variant taking a map and returning the result as a string, also added a log5() variant returning the result instead of printing it to the standard output.

            – icza
            Nov 25 '16 at 20:42











          • @icza: Thanks for an awesome answer. I wonder if, almost a year later, there's a better way of doing this? In the end, I went with strings.Replacer, and it seems to work fine.

            – elo80ka
            Oct 28 '17 at 13:26













          • @elo80ka Not that I know of. You have the same options as a year before.

            – icza
            Oct 30 '17 at 9:48



















          11














          I don't know of any easy way of naming the parameters, but you can easily change the order of the arguments, using explicit argument indexes:



          From docs:




          In Printf, Sprintf, and Fprintf, the default behavior is for each formatting verb to format successive arguments passed in the call. However, the notation [n] immediately before the verb indicates that the nth one-indexed argument is to be formatted instead. The same notation before a '*' for a width or precision selects the argument index holding the value. After processing a bracketed expression [n], subsequent verbs will use arguments n+1, n+2, etc. unless otherwise directed.




          Then you can, ie:



          fmt.Printf("File %[2]s had error %[1]s", err, myfile)





          share|improve this answer































            3














            The parameter can also be a map, so the following function would work if you don't mind parsing every error format every time you use it:



            package main

            import (
            "bytes"
            "text/template"
            "fmt"
            )

            func msg(fmt string, args map[string]interface{}) (str string) {
            var msg bytes.Buffer

            tmpl, err := template.New("errmsg").Parse(fmt)

            if err != nil {
            return fmt
            }

            tmpl.Execute(&msg, args)
            return msg.String()
            }

            func main() {
            fmt.Printf(msg("File {{.File}} has error {{.Error}}n", map[string]interface{} {
            "File": "abc",
            "Error": "def",
            }))
            }


            It's still a little wordier than I would have liked, but it's better than some other options, I suppose. You could turn map[string]interface{} into a local type and reduce it further to:



            type P map[string]interface{}

            fmt.Printf(msg("File {{.File}} has error {{.Error}}n", P{
            "File": "abc",
            "Error": "def",
            }))





            share|improve this answer































              1














              Alas, there's no built-in function in Go for string interpolation with named parameters (yet). But you are not the only one suffering out there :) Some packages should exist, for example: https://github.com/imkira/go-interpol . Or, if feeling adventurous, you could write such a helper yourself, as the concept is actually quite simple.



              Cheers,
              Dennis






              share|improve this answer























                Your Answer






                StackExchange.ifUsing("editor", function () {
                StackExchange.using("externalEditor", function () {
                StackExchange.using("snippets", function () {
                StackExchange.snippets.init();
                });
                });
                }, "code-snippets");

                StackExchange.ready(function() {
                var channelOptions = {
                tags: "".split(" "),
                id: "1"
                };
                initTagRenderer("".split(" "), "".split(" "), channelOptions);

                StackExchange.using("externalEditor", function() {
                // Have to fire editor after snippets, if snippets enabled
                if (StackExchange.settings.snippets.snippetsEnabled) {
                StackExchange.using("snippets", function() {
                createEditor();
                });
                }
                else {
                createEditor();
                }
                });

                function createEditor() {
                StackExchange.prepareEditor({
                heartbeatType: 'answer',
                autoActivateHeartbeat: false,
                convertImagesToLinks: true,
                noModals: true,
                showLowRepImageUploadWarning: true,
                reputationToPostImages: 10,
                bindNavPrevention: true,
                postfix: "",
                imageUploader: {
                brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
                contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
                allowUrls: true
                },
                onDemand: true,
                discardSelector: ".discard-answer"
                ,immediatelyShowMarkdownHelp:true
                });


                }
                });














                draft saved

                draft discarded


















                StackExchange.ready(
                function () {
                StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f40811117%2fequivalent-of-python-string-format-in-go%23new-answer', 'question_page');
                }
                );

                Post as a guest















                Required, but never shown

























                4 Answers
                4






                active

                oldest

                votes








                4 Answers
                4






                active

                oldest

                votes









                active

                oldest

                votes






                active

                oldest

                votes









                29














                With strings.Replacer



                Using strings.Replacer, implementing a formatter of your desire is very easy and compact.



                func main() {
                file, err := "/data/test.txt", "file not found"

                log("File {file} had error {error}", "{file}", file, "{error}", err)
                }

                func log(format string, args ...string) {
                r := strings.NewReplacer(args...)
                fmt.Println(r.Replace(format))
                }


                Output (try it on the Go Playground):



                File /data/test.txt had error file not found


                We can make it more pleasant to use by adding the brackets to the parameter names automatically in the log() function:



                func main() {
                file, err := "/data/test.txt", "file not found"

                log2("File {file} had error {error}", "file", file, "error", err)
                }

                func log2(format string, args ...string) {
                for i, v := range args {
                if i%2 == 0 {
                args[i] = "{" + v + "}"
                }
                }
                r := strings.NewReplacer(args...)
                fmt.Println(r.Replace(format))
                }


                Output (try it on the Go Playground):



                File /data/test.txt had error file not found


                Yes, you could say that this only accepts string parameter values. This is true. With a little more improvement, this won't be true:



                func main() {
                file, err := "/data/test.txt", 666

                log3("File {file} had error {error}", "file", file, "error", err)
                }

                func log3(format string, args ...interface{}) {
                args2 := make(string, len(args))
                for i, v := range args {
                if i%2 == 0 {
                args2[i] = fmt.Sprintf("{%v}", v)
                } else {
                args2[i] = fmt.Sprint(v)
                }
                }
                r := strings.NewReplacer(args2...)
                fmt.Println(r.Replace(format))
                }


                Output (try it on the Go Playground):



                File /data/test.txt had error 666


                A variant of this to accept params as a map[string]interface{} and return the result as a string:



                type P map[string]interface{}

                func main() {
                file, err := "/data/test.txt", 666

                s := log33("File {file} had error {error}", P{"file": file, "error": err})
                fmt.Println(s)
                }

                func log33(format string, p P) string {
                args, i := make(string, len(p)*2), 0
                for k, v := range p {
                args[i] = "{" + k + "}"
                args[i+1] = fmt.Sprint(v)
                i += 2
                }
                return strings.NewReplacer(args...).Replace(format)
                }


                Try it on the Go Playground.



                With text/template



                Your template solution or proposal is also way too verbose. It can be written as compact as this (error checks omitted):



                type P map[string]interface{}

                func main() {
                file, err := "/data/test.txt", 666

                log4("File {{.file}} has error {{.error}}", P{"file": file, "error": err})
                }

                func log4(format string, p P) {
                t := template.Must(template.New("").Parse(format))
                t.Execute(os.Stdout, p)
                }


                Output (try it on the Go Playground):



                File /data/test.txt has error 666


                If you want to return the string (instead of printing it to the standard output), you may do it like this (try it on the Go Playground):



                func log5(format string, p P) string {
                b := &bytes.Buffer{}
                template.Must(template.New("").Parse(format)).Execute(b, p)
                return b.String()
                }


                Using explicit argument indices



                This was already mentioned in another answer, but to complete it, know that the same explicit argument index may be used arbitrary number of times and thus resulting in the same parameter substituted in multiple times. Read more about this in this question: Replace all variables in Sprintf with same variable






                share|improve this answer


























                • I suppose that you could also combine the approach in my answer using a map, below, with your approach in log3, putting the args into a map instead of a string array and then formatting it as you do with log4. A lot of the complication in my example was to achieve returning a string. Could you modify log4 to accommodate that?

                  – Scott Deerwester
                  Nov 25 '16 at 20:30











                • @ScottDeerwester Yes, added a log33() variant taking a map and returning the result as a string, also added a log5() variant returning the result instead of printing it to the standard output.

                  – icza
                  Nov 25 '16 at 20:42











                • @icza: Thanks for an awesome answer. I wonder if, almost a year later, there's a better way of doing this? In the end, I went with strings.Replacer, and it seems to work fine.

                  – elo80ka
                  Oct 28 '17 at 13:26













                • @elo80ka Not that I know of. You have the same options as a year before.

                  – icza
                  Oct 30 '17 at 9:48
















                29














                With strings.Replacer



                Using strings.Replacer, implementing a formatter of your desire is very easy and compact.



                func main() {
                file, err := "/data/test.txt", "file not found"

                log("File {file} had error {error}", "{file}", file, "{error}", err)
                }

                func log(format string, args ...string) {
                r := strings.NewReplacer(args...)
                fmt.Println(r.Replace(format))
                }


                Output (try it on the Go Playground):



                File /data/test.txt had error file not found


                We can make it more pleasant to use by adding the brackets to the parameter names automatically in the log() function:



                func main() {
                file, err := "/data/test.txt", "file not found"

                log2("File {file} had error {error}", "file", file, "error", err)
                }

                func log2(format string, args ...string) {
                for i, v := range args {
                if i%2 == 0 {
                args[i] = "{" + v + "}"
                }
                }
                r := strings.NewReplacer(args...)
                fmt.Println(r.Replace(format))
                }


                Output (try it on the Go Playground):



                File /data/test.txt had error file not found


                Yes, you could say that this only accepts string parameter values. This is true. With a little more improvement, this won't be true:



                func main() {
                file, err := "/data/test.txt", 666

                log3("File {file} had error {error}", "file", file, "error", err)
                }

                func log3(format string, args ...interface{}) {
                args2 := make(string, len(args))
                for i, v := range args {
                if i%2 == 0 {
                args2[i] = fmt.Sprintf("{%v}", v)
                } else {
                args2[i] = fmt.Sprint(v)
                }
                }
                r := strings.NewReplacer(args2...)
                fmt.Println(r.Replace(format))
                }


                Output (try it on the Go Playground):



                File /data/test.txt had error 666


                A variant of this to accept params as a map[string]interface{} and return the result as a string:



                type P map[string]interface{}

                func main() {
                file, err := "/data/test.txt", 666

                s := log33("File {file} had error {error}", P{"file": file, "error": err})
                fmt.Println(s)
                }

                func log33(format string, p P) string {
                args, i := make(string, len(p)*2), 0
                for k, v := range p {
                args[i] = "{" + k + "}"
                args[i+1] = fmt.Sprint(v)
                i += 2
                }
                return strings.NewReplacer(args...).Replace(format)
                }


                Try it on the Go Playground.



                With text/template



                Your template solution or proposal is also way too verbose. It can be written as compact as this (error checks omitted):



                type P map[string]interface{}

                func main() {
                file, err := "/data/test.txt", 666

                log4("File {{.file}} has error {{.error}}", P{"file": file, "error": err})
                }

                func log4(format string, p P) {
                t := template.Must(template.New("").Parse(format))
                t.Execute(os.Stdout, p)
                }


                Output (try it on the Go Playground):



                File /data/test.txt has error 666


                If you want to return the string (instead of printing it to the standard output), you may do it like this (try it on the Go Playground):



                func log5(format string, p P) string {
                b := &bytes.Buffer{}
                template.Must(template.New("").Parse(format)).Execute(b, p)
                return b.String()
                }


                Using explicit argument indices



                This was already mentioned in another answer, but to complete it, know that the same explicit argument index may be used arbitrary number of times and thus resulting in the same parameter substituted in multiple times. Read more about this in this question: Replace all variables in Sprintf with same variable






                share|improve this answer


























                • I suppose that you could also combine the approach in my answer using a map, below, with your approach in log3, putting the args into a map instead of a string array and then formatting it as you do with log4. A lot of the complication in my example was to achieve returning a string. Could you modify log4 to accommodate that?

                  – Scott Deerwester
                  Nov 25 '16 at 20:30











                • @ScottDeerwester Yes, added a log33() variant taking a map and returning the result as a string, also added a log5() variant returning the result instead of printing it to the standard output.

                  – icza
                  Nov 25 '16 at 20:42











                • @icza: Thanks for an awesome answer. I wonder if, almost a year later, there's a better way of doing this? In the end, I went with strings.Replacer, and it seems to work fine.

                  – elo80ka
                  Oct 28 '17 at 13:26













                • @elo80ka Not that I know of. You have the same options as a year before.

                  – icza
                  Oct 30 '17 at 9:48














                29












                29








                29







                With strings.Replacer



                Using strings.Replacer, implementing a formatter of your desire is very easy and compact.



                func main() {
                file, err := "/data/test.txt", "file not found"

                log("File {file} had error {error}", "{file}", file, "{error}", err)
                }

                func log(format string, args ...string) {
                r := strings.NewReplacer(args...)
                fmt.Println(r.Replace(format))
                }


                Output (try it on the Go Playground):



                File /data/test.txt had error file not found


                We can make it more pleasant to use by adding the brackets to the parameter names automatically in the log() function:



                func main() {
                file, err := "/data/test.txt", "file not found"

                log2("File {file} had error {error}", "file", file, "error", err)
                }

                func log2(format string, args ...string) {
                for i, v := range args {
                if i%2 == 0 {
                args[i] = "{" + v + "}"
                }
                }
                r := strings.NewReplacer(args...)
                fmt.Println(r.Replace(format))
                }


                Output (try it on the Go Playground):



                File /data/test.txt had error file not found


                Yes, you could say that this only accepts string parameter values. This is true. With a little more improvement, this won't be true:



                func main() {
                file, err := "/data/test.txt", 666

                log3("File {file} had error {error}", "file", file, "error", err)
                }

                func log3(format string, args ...interface{}) {
                args2 := make(string, len(args))
                for i, v := range args {
                if i%2 == 0 {
                args2[i] = fmt.Sprintf("{%v}", v)
                } else {
                args2[i] = fmt.Sprint(v)
                }
                }
                r := strings.NewReplacer(args2...)
                fmt.Println(r.Replace(format))
                }


                Output (try it on the Go Playground):



                File /data/test.txt had error 666


                A variant of this to accept params as a map[string]interface{} and return the result as a string:



                type P map[string]interface{}

                func main() {
                file, err := "/data/test.txt", 666

                s := log33("File {file} had error {error}", P{"file": file, "error": err})
                fmt.Println(s)
                }

                func log33(format string, p P) string {
                args, i := make(string, len(p)*2), 0
                for k, v := range p {
                args[i] = "{" + k + "}"
                args[i+1] = fmt.Sprint(v)
                i += 2
                }
                return strings.NewReplacer(args...).Replace(format)
                }


                Try it on the Go Playground.



                With text/template



                Your template solution or proposal is also way too verbose. It can be written as compact as this (error checks omitted):



                type P map[string]interface{}

                func main() {
                file, err := "/data/test.txt", 666

                log4("File {{.file}} has error {{.error}}", P{"file": file, "error": err})
                }

                func log4(format string, p P) {
                t := template.Must(template.New("").Parse(format))
                t.Execute(os.Stdout, p)
                }


                Output (try it on the Go Playground):



                File /data/test.txt has error 666


                If you want to return the string (instead of printing it to the standard output), you may do it like this (try it on the Go Playground):



                func log5(format string, p P) string {
                b := &bytes.Buffer{}
                template.Must(template.New("").Parse(format)).Execute(b, p)
                return b.String()
                }


                Using explicit argument indices



                This was already mentioned in another answer, but to complete it, know that the same explicit argument index may be used arbitrary number of times and thus resulting in the same parameter substituted in multiple times. Read more about this in this question: Replace all variables in Sprintf with same variable






                share|improve this answer















                With strings.Replacer



                Using strings.Replacer, implementing a formatter of your desire is very easy and compact.



                func main() {
                file, err := "/data/test.txt", "file not found"

                log("File {file} had error {error}", "{file}", file, "{error}", err)
                }

                func log(format string, args ...string) {
                r := strings.NewReplacer(args...)
                fmt.Println(r.Replace(format))
                }


                Output (try it on the Go Playground):



                File /data/test.txt had error file not found


                We can make it more pleasant to use by adding the brackets to the parameter names automatically in the log() function:



                func main() {
                file, err := "/data/test.txt", "file not found"

                log2("File {file} had error {error}", "file", file, "error", err)
                }

                func log2(format string, args ...string) {
                for i, v := range args {
                if i%2 == 0 {
                args[i] = "{" + v + "}"
                }
                }
                r := strings.NewReplacer(args...)
                fmt.Println(r.Replace(format))
                }


                Output (try it on the Go Playground):



                File /data/test.txt had error file not found


                Yes, you could say that this only accepts string parameter values. This is true. With a little more improvement, this won't be true:



                func main() {
                file, err := "/data/test.txt", 666

                log3("File {file} had error {error}", "file", file, "error", err)
                }

                func log3(format string, args ...interface{}) {
                args2 := make(string, len(args))
                for i, v := range args {
                if i%2 == 0 {
                args2[i] = fmt.Sprintf("{%v}", v)
                } else {
                args2[i] = fmt.Sprint(v)
                }
                }
                r := strings.NewReplacer(args2...)
                fmt.Println(r.Replace(format))
                }


                Output (try it on the Go Playground):



                File /data/test.txt had error 666


                A variant of this to accept params as a map[string]interface{} and return the result as a string:



                type P map[string]interface{}

                func main() {
                file, err := "/data/test.txt", 666

                s := log33("File {file} had error {error}", P{"file": file, "error": err})
                fmt.Println(s)
                }

                func log33(format string, p P) string {
                args, i := make(string, len(p)*2), 0
                for k, v := range p {
                args[i] = "{" + k + "}"
                args[i+1] = fmt.Sprint(v)
                i += 2
                }
                return strings.NewReplacer(args...).Replace(format)
                }


                Try it on the Go Playground.



                With text/template



                Your template solution or proposal is also way too verbose. It can be written as compact as this (error checks omitted):



                type P map[string]interface{}

                func main() {
                file, err := "/data/test.txt", 666

                log4("File {{.file}} has error {{.error}}", P{"file": file, "error": err})
                }

                func log4(format string, p P) {
                t := template.Must(template.New("").Parse(format))
                t.Execute(os.Stdout, p)
                }


                Output (try it on the Go Playground):



                File /data/test.txt has error 666


                If you want to return the string (instead of printing it to the standard output), you may do it like this (try it on the Go Playground):



                func log5(format string, p P) string {
                b := &bytes.Buffer{}
                template.Must(template.New("").Parse(format)).Execute(b, p)
                return b.String()
                }


                Using explicit argument indices



                This was already mentioned in another answer, but to complete it, know that the same explicit argument index may be used arbitrary number of times and thus resulting in the same parameter substituted in multiple times. Read more about this in this question: Replace all variables in Sprintf with same variable







                share|improve this answer














                share|improve this answer



                share|improve this answer








                edited May 23 '17 at 11:46









                Community

                11




                11










                answered Nov 25 '16 at 20:01









                iczaicza

                166k25330364




                166k25330364













                • I suppose that you could also combine the approach in my answer using a map, below, with your approach in log3, putting the args into a map instead of a string array and then formatting it as you do with log4. A lot of the complication in my example was to achieve returning a string. Could you modify log4 to accommodate that?

                  – Scott Deerwester
                  Nov 25 '16 at 20:30











                • @ScottDeerwester Yes, added a log33() variant taking a map and returning the result as a string, also added a log5() variant returning the result instead of printing it to the standard output.

                  – icza
                  Nov 25 '16 at 20:42











                • @icza: Thanks for an awesome answer. I wonder if, almost a year later, there's a better way of doing this? In the end, I went with strings.Replacer, and it seems to work fine.

                  – elo80ka
                  Oct 28 '17 at 13:26













                • @elo80ka Not that I know of. You have the same options as a year before.

                  – icza
                  Oct 30 '17 at 9:48



















                • I suppose that you could also combine the approach in my answer using a map, below, with your approach in log3, putting the args into a map instead of a string array and then formatting it as you do with log4. A lot of the complication in my example was to achieve returning a string. Could you modify log4 to accommodate that?

                  – Scott Deerwester
                  Nov 25 '16 at 20:30











                • @ScottDeerwester Yes, added a log33() variant taking a map and returning the result as a string, also added a log5() variant returning the result instead of printing it to the standard output.

                  – icza
                  Nov 25 '16 at 20:42











                • @icza: Thanks for an awesome answer. I wonder if, almost a year later, there's a better way of doing this? In the end, I went with strings.Replacer, and it seems to work fine.

                  – elo80ka
                  Oct 28 '17 at 13:26













                • @elo80ka Not that I know of. You have the same options as a year before.

                  – icza
                  Oct 30 '17 at 9:48

















                I suppose that you could also combine the approach in my answer using a map, below, with your approach in log3, putting the args into a map instead of a string array and then formatting it as you do with log4. A lot of the complication in my example was to achieve returning a string. Could you modify log4 to accommodate that?

                – Scott Deerwester
                Nov 25 '16 at 20:30





                I suppose that you could also combine the approach in my answer using a map, below, with your approach in log3, putting the args into a map instead of a string array and then formatting it as you do with log4. A lot of the complication in my example was to achieve returning a string. Could you modify log4 to accommodate that?

                – Scott Deerwester
                Nov 25 '16 at 20:30













                @ScottDeerwester Yes, added a log33() variant taking a map and returning the result as a string, also added a log5() variant returning the result instead of printing it to the standard output.

                – icza
                Nov 25 '16 at 20:42





                @ScottDeerwester Yes, added a log33() variant taking a map and returning the result as a string, also added a log5() variant returning the result instead of printing it to the standard output.

                – icza
                Nov 25 '16 at 20:42













                @icza: Thanks for an awesome answer. I wonder if, almost a year later, there's a better way of doing this? In the end, I went with strings.Replacer, and it seems to work fine.

                – elo80ka
                Oct 28 '17 at 13:26







                @icza: Thanks for an awesome answer. I wonder if, almost a year later, there's a better way of doing this? In the end, I went with strings.Replacer, and it seems to work fine.

                – elo80ka
                Oct 28 '17 at 13:26















                @elo80ka Not that I know of. You have the same options as a year before.

                – icza
                Oct 30 '17 at 9:48





                @elo80ka Not that I know of. You have the same options as a year before.

                – icza
                Oct 30 '17 at 9:48













                11














                I don't know of any easy way of naming the parameters, but you can easily change the order of the arguments, using explicit argument indexes:



                From docs:




                In Printf, Sprintf, and Fprintf, the default behavior is for each formatting verb to format successive arguments passed in the call. However, the notation [n] immediately before the verb indicates that the nth one-indexed argument is to be formatted instead. The same notation before a '*' for a width or precision selects the argument index holding the value. After processing a bracketed expression [n], subsequent verbs will use arguments n+1, n+2, etc. unless otherwise directed.




                Then you can, ie:



                fmt.Printf("File %[2]s had error %[1]s", err, myfile)





                share|improve this answer




























                  11














                  I don't know of any easy way of naming the parameters, but you can easily change the order of the arguments, using explicit argument indexes:



                  From docs:




                  In Printf, Sprintf, and Fprintf, the default behavior is for each formatting verb to format successive arguments passed in the call. However, the notation [n] immediately before the verb indicates that the nth one-indexed argument is to be formatted instead. The same notation before a '*' for a width or precision selects the argument index holding the value. After processing a bracketed expression [n], subsequent verbs will use arguments n+1, n+2, etc. unless otherwise directed.




                  Then you can, ie:



                  fmt.Printf("File %[2]s had error %[1]s", err, myfile)





                  share|improve this answer


























                    11












                    11








                    11







                    I don't know of any easy way of naming the parameters, but you can easily change the order of the arguments, using explicit argument indexes:



                    From docs:




                    In Printf, Sprintf, and Fprintf, the default behavior is for each formatting verb to format successive arguments passed in the call. However, the notation [n] immediately before the verb indicates that the nth one-indexed argument is to be formatted instead. The same notation before a '*' for a width or precision selects the argument index holding the value. After processing a bracketed expression [n], subsequent verbs will use arguments n+1, n+2, etc. unless otherwise directed.




                    Then you can, ie:



                    fmt.Printf("File %[2]s had error %[1]s", err, myfile)





                    share|improve this answer













                    I don't know of any easy way of naming the parameters, but you can easily change the order of the arguments, using explicit argument indexes:



                    From docs:




                    In Printf, Sprintf, and Fprintf, the default behavior is for each formatting verb to format successive arguments passed in the call. However, the notation [n] immediately before the verb indicates that the nth one-indexed argument is to be formatted instead. The same notation before a '*' for a width or precision selects the argument index holding the value. After processing a bracketed expression [n], subsequent verbs will use arguments n+1, n+2, etc. unless otherwise directed.




                    Then you can, ie:



                    fmt.Printf("File %[2]s had error %[1]s", err, myfile)






                    share|improve this answer












                    share|improve this answer



                    share|improve this answer










                    answered Nov 25 '16 at 19:32









                    hlscalonhlscalon

                    5,44232338




                    5,44232338























                        3














                        The parameter can also be a map, so the following function would work if you don't mind parsing every error format every time you use it:



                        package main

                        import (
                        "bytes"
                        "text/template"
                        "fmt"
                        )

                        func msg(fmt string, args map[string]interface{}) (str string) {
                        var msg bytes.Buffer

                        tmpl, err := template.New("errmsg").Parse(fmt)

                        if err != nil {
                        return fmt
                        }

                        tmpl.Execute(&msg, args)
                        return msg.String()
                        }

                        func main() {
                        fmt.Printf(msg("File {{.File}} has error {{.Error}}n", map[string]interface{} {
                        "File": "abc",
                        "Error": "def",
                        }))
                        }


                        It's still a little wordier than I would have liked, but it's better than some other options, I suppose. You could turn map[string]interface{} into a local type and reduce it further to:



                        type P map[string]interface{}

                        fmt.Printf(msg("File {{.File}} has error {{.Error}}n", P{
                        "File": "abc",
                        "Error": "def",
                        }))





                        share|improve this answer




























                          3














                          The parameter can also be a map, so the following function would work if you don't mind parsing every error format every time you use it:



                          package main

                          import (
                          "bytes"
                          "text/template"
                          "fmt"
                          )

                          func msg(fmt string, args map[string]interface{}) (str string) {
                          var msg bytes.Buffer

                          tmpl, err := template.New("errmsg").Parse(fmt)

                          if err != nil {
                          return fmt
                          }

                          tmpl.Execute(&msg, args)
                          return msg.String()
                          }

                          func main() {
                          fmt.Printf(msg("File {{.File}} has error {{.Error}}n", map[string]interface{} {
                          "File": "abc",
                          "Error": "def",
                          }))
                          }


                          It's still a little wordier than I would have liked, but it's better than some other options, I suppose. You could turn map[string]interface{} into a local type and reduce it further to:



                          type P map[string]interface{}

                          fmt.Printf(msg("File {{.File}} has error {{.Error}}n", P{
                          "File": "abc",
                          "Error": "def",
                          }))





                          share|improve this answer


























                            3












                            3








                            3







                            The parameter can also be a map, so the following function would work if you don't mind parsing every error format every time you use it:



                            package main

                            import (
                            "bytes"
                            "text/template"
                            "fmt"
                            )

                            func msg(fmt string, args map[string]interface{}) (str string) {
                            var msg bytes.Buffer

                            tmpl, err := template.New("errmsg").Parse(fmt)

                            if err != nil {
                            return fmt
                            }

                            tmpl.Execute(&msg, args)
                            return msg.String()
                            }

                            func main() {
                            fmt.Printf(msg("File {{.File}} has error {{.Error}}n", map[string]interface{} {
                            "File": "abc",
                            "Error": "def",
                            }))
                            }


                            It's still a little wordier than I would have liked, but it's better than some other options, I suppose. You could turn map[string]interface{} into a local type and reduce it further to:



                            type P map[string]interface{}

                            fmt.Printf(msg("File {{.File}} has error {{.Error}}n", P{
                            "File": "abc",
                            "Error": "def",
                            }))





                            share|improve this answer













                            The parameter can also be a map, so the following function would work if you don't mind parsing every error format every time you use it:



                            package main

                            import (
                            "bytes"
                            "text/template"
                            "fmt"
                            )

                            func msg(fmt string, args map[string]interface{}) (str string) {
                            var msg bytes.Buffer

                            tmpl, err := template.New("errmsg").Parse(fmt)

                            if err != nil {
                            return fmt
                            }

                            tmpl.Execute(&msg, args)
                            return msg.String()
                            }

                            func main() {
                            fmt.Printf(msg("File {{.File}} has error {{.Error}}n", map[string]interface{} {
                            "File": "abc",
                            "Error": "def",
                            }))
                            }


                            It's still a little wordier than I would have liked, but it's better than some other options, I suppose. You could turn map[string]interface{} into a local type and reduce it further to:



                            type P map[string]interface{}

                            fmt.Printf(msg("File {{.File}} has error {{.Error}}n", P{
                            "File": "abc",
                            "Error": "def",
                            }))






                            share|improve this answer












                            share|improve this answer



                            share|improve this answer










                            answered Nov 25 '16 at 19:44









                            Scott DeerwesterScott Deerwester

                            97111027




                            97111027























                                1














                                Alas, there's no built-in function in Go for string interpolation with named parameters (yet). But you are not the only one suffering out there :) Some packages should exist, for example: https://github.com/imkira/go-interpol . Or, if feeling adventurous, you could write such a helper yourself, as the concept is actually quite simple.



                                Cheers,
                                Dennis






                                share|improve this answer




























                                  1














                                  Alas, there's no built-in function in Go for string interpolation with named parameters (yet). But you are not the only one suffering out there :) Some packages should exist, for example: https://github.com/imkira/go-interpol . Or, if feeling adventurous, you could write such a helper yourself, as the concept is actually quite simple.



                                  Cheers,
                                  Dennis






                                  share|improve this answer


























                                    1












                                    1








                                    1







                                    Alas, there's no built-in function in Go for string interpolation with named parameters (yet). But you are not the only one suffering out there :) Some packages should exist, for example: https://github.com/imkira/go-interpol . Or, if feeling adventurous, you could write such a helper yourself, as the concept is actually quite simple.



                                    Cheers,
                                    Dennis






                                    share|improve this answer













                                    Alas, there's no built-in function in Go for string interpolation with named parameters (yet). But you are not the only one suffering out there :) Some packages should exist, for example: https://github.com/imkira/go-interpol . Or, if feeling adventurous, you could write such a helper yourself, as the concept is actually quite simple.



                                    Cheers,
                                    Dennis







                                    share|improve this answer












                                    share|improve this answer



                                    share|improve this answer










                                    answered Nov 25 '16 at 20:05









                                    oharlemoharlem

                                    790411




                                    790411






























                                        draft saved

                                        draft discarded




















































                                        Thanks for contributing an answer to Stack Overflow!


                                        • Please be sure to answer the question. Provide details and share your research!

                                        But avoid



                                        • Asking for help, clarification, or responding to other answers.

                                        • Making statements based on opinion; back them up with references or personal experience.


                                        To learn more, see our tips on writing great answers.




                                        draft saved


                                        draft discarded














                                        StackExchange.ready(
                                        function () {
                                        StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f40811117%2fequivalent-of-python-string-format-in-go%23new-answer', 'question_page');
                                        }
                                        );

                                        Post as a guest















                                        Required, but never shown





















































                                        Required, but never shown














                                        Required, but never shown












                                        Required, but never shown







                                        Required, but never shown

































                                        Required, but never shown














                                        Required, but never shown












                                        Required, but never shown







                                        Required, but never shown







                                        Popular posts from this blog

                                        Monofisismo

                                        Angular Downloading a file using contenturl with Basic Authentication

                                        Olmecas