I am trying to write a functional that wraps FPrintf however I keep getting weird characters out.
Here is an reproducer
https://play.golang.org/p/yZgNnpovEa
The idea is to be able to have a conditional Printf which I can test the output (Thus use of FPrintf so I can test the output towards the input). Is there any way to get around this?
I have seen How to ignore extra fields for fmt.Sprintf but all answers there assume the user is expecting only %s while in my case I want to be as flexible as Printf and the only other one is downvoted.
Is this just not possible and can anybody give a reasonable explanation why?
The problem is you actually send an empty slice to fmt.Fprintf.
Additional check of params' length should fix the problem.
func (p ConditionalPrinter) printF(s string, params ...interface{}) {
if p.print {
if len(params) == 0 {
fmt.Fprintf(p.writer, s)
} else {
fmt.Fprintf(p.writer, s, params)
}
}
}
Or this:
func (p ConditionalPrinter) printF(s string, params ...interface{}) {
switch {
case !p.print:
return
case len(params) == 0:
fmt.Fprintf(p.writer, s)
default:
fmt.Fprintf(p.writer, s, params)
}
}
See Playground link
Related
Absolute newbie question here.
Some functions in Go return more than one value (normally, the value and an error). I was writing a func who return the return value of one of those functions, and even if it is very easy to put the values on variables and return only the first one, I have the doubt if I could do the same in only one line without the extra variable. This is something often uses in other languages like C, Java, C#, Ruby, etc
func someFunc (param string) int {
// do something with the string, not very important
return strconv.Atoi(param)
}
I know this works
func someFunc (param string) int {
// do something with the string, not very important
var result int
result, _ = strconv.Atoi(param)
return result
}
It is this possible in Go? It is considered a "good practice" (like in Java*)
Note: Before someone say that this technique is not a good practice in Java, clarify that is not important for the question, but some people (like the ones in the company I work) encourage that style.
Use a short variable declaration for the shortest code to accomplish this goal:
func SomeFunc(parm string) int {
result, _ := strconv.Atoi(param)
return result
}
There is no one line solution without introducing a helper function that accepts two arguments and returns the first. One of these helper functions would be needed for each combination of types where you want to ignore a value.
Your best possible one-liner is a helper function written as:
func first(n int, _ error) int {
return n
}
func SomeFunc(param string) int {
return first(strconv.Atoi(param))
}
Note that:
the argument types and positions must match exactly
the second argument to first has the blank identifier (_), making it clear that you wish to completely ignore it. [1]
If you absolutely don't want to declare a named function, you may use a function literal, but that looks real ugly:
func SomeFunc(param string) int {
return func(n int, _ error) int { return n }(strconv.Atoi(param))
}
In general, the helper function is worth it if you have a lot of repetition in your code. Otherwise just use a temp variable, which looks clean and idiomatic:
func SomeFunc(param string) int {
n, _ := strconv.Atoi(param)
return n
}
Playground: https://play.golang.org/p/X8EOh_JVDDG
Once generics will be added to the language in Go 1.18, you will be able to write a helper function that can be used with any return pair and preserve type safety on the first one:
func first[T, U any](val T, _ U) T {
return val
}
func SomeFunc(param string) int {
return first(strconv.Atoi(param))
}
Go2 Playground: https://go2goplay.golang.org/p/vLmTuwzrl5o
Footnotes:
[1] Keep in mind that in case of strings.Atoi the second return value is an error, and ignoring errors is bad practice. However there are cases where the success of the operation truly doesn't matter, then it's fine to use _ to ignore the argument.
I'm trying to implement a default value according to the option 1 of the post Golang and default values. But when I try to do go install the following error pops up in the terminal:
not enough arguments in call to test.Concat1
have ()
want (string)
Code:
package test
func Concat1(a string) string {
if a == "" {
a = "default-a"
}
return fmt.Sprintf("%s", a)
}
// other package
package main
func main() {
test.Concat1()
}
Thanks in advance.
I don't think what you are trying to do will work that way. You may want to opt for option #4 from the page you cited, which uses variadic variables. In your case looks to me like you want just a string, so it'd be something like this:
func Concat1(a ...string) string {
if len(a) == 0 {
return "a-default"
}
return a[0]
}
Go does not have optional defaults for function arguments.
You may emulate them to some extent by having a special type
to contain the set of parameters for a function.
In your toy example that would be something like
type Concat1Args struct {
a string
}
func Concat1(args Concat1Args) string {
if args.a == "" {
args.a = "default-a"
}
return fmt.Sprintf("%s", args.a)
}
The "trick" here is that in Go each type has its respective
"zero value", and when producing a value of a composite type
using the so-called literal, it's possible to initialize only some of the type's fields, so in our example that would be
s := Concat1(Concat1Args{})
vs
s := Concat1(Concat1Args{"whatever"})
I know that looks clumsy, and I have showed this mostly for
demonstration purpose. In real production code, where a function
might have a dozen of parameters or more, having them packed
in a dedicate composite type is usually the only sensible way
to go but for a case like yours it's better to just explicitly
pass "" to the function.
Golang does not support default parameters. Accordingly, variadic arguments by themselves are not analogous. However, variadic functions with the use of error handling can 'resemble' the pattern. Try the following as a simple example:
package main
import (
"errors"
"log"
)
func createSeries(p ...int) ([]int, error) {
usage := "Usage: createSeries(<length>, <optional starting value>), length should be > 0"
if len(p) == 0 {
return nil, errors.New(usage)
}
n := p[0]
if n <= 0 {
return nil, errors.New(usage)
}
var base int
if len(p) == 2 {
base = p[1]
} else if len(p) > 2 {
return nil, errors.New(usage)
}
vals := make([]int, n)
for i := 0; i < n; i++ {
vals[i] = base + i
}
return vals, nil
}
func main() {
answer, err := createSeries(4, -9)
if err != nil {
log.Fatal(err)
}
log.Println(answer)
}
Default parameters work differently in Go than they do in other languages. In a function there can be one ellipsis, always at the end, which will keep a slice of values of the same type so in your case this would be:
func Concat1(a ...string) string {
but that means that the caller may pass in any number of arguments >= 0. Also you need to check that the arguments in the slice are not empty and then assign them yourself. This means they do not get assigned a default value through any kind of special syntax in Go. This is not possible but you can do
if a[0] == "" {
a[0] = "default value"
}
If you want to make sure that the user passes either zero or one strings, just create two functions in your API, e.g.
func Concat(a string) string { // ...
func ConcatDefault() string {
Concat("default value")
}
I'm new with Go syntax, just trying to pass an error if the user did not input anything...
[EDIT] I would like the following function to stop running if the user did not input anything, and print a log. My if required && answer == "" statement doesn't seem to work as "You must enter a value." does not log when the user does not input anything.
func QAR(q string, r string, required bool) string {
reader := bufio.NewReader(os.Stdin)
// Print the question
fmt.Printf(q)
answer, _ := reader.ReadString('\n')
// If the answer is empty, return a log
if required && answer == "" {
log.Fatal("You must enter a value.")
// Can I just use return to block it?
return
}
// Print the response with the answer
if r != "" {
fmt.Println(r, answer)
}
return answer
}
The typical pattern in go is to return multiple values, the last of which is an error, if one occurred. So your function signature could look like this:
func QAR(q string, r string, required bool) (string, error)
And the return statements like this:
return "", fmt.Errorf("user provided no input")
// ...
return answer, nil
[EDIT]
Note that bufio.Reader.ReadString(...) includes the delimiter, so you probably need to check if answer == "\n".
I have a simple function I want to test:
func (t *Thing) print(min_verbosity int, message string) {
if t.verbosity >= minv {
fmt.Print(message)
}
}
But how can I test what the function actually sends to standard output? Test::Output does what I want in Perl. I know I could write all my own boilerplate to do the same in Go (as described here):
orig = os.Stdout
r,w,_ = os.Pipe()
thing.print("Some message")
var buf bytes.Buffer
io.Copy(&buf, r)
w.Close()
os.Stdout = orig
if(buf.String() != "Some message") {
t.Error("Failure!")
}
But that's a lot of extra work for every single test. I'm hoping there's a more standard way, or perhaps an abstraction library to handle this.
One thing to also remember, there's nothing stopping you from writing functions to avoid the boilerplate.
For example I have a command line app that uses log and I wrote this function:
func captureOutput(f func()) string {
var buf bytes.Buffer
log.SetOutput(&buf)
f()
log.SetOutput(os.Stderr)
return buf.String()
}
Then used it like this:
output := captureOutput(func() {
client.RemoveCertificate("www.example.com")
})
assert.Equal(t, "removed certificate www.example.com\n", output)
Using this assert library: http://godoc.org/github.com/stretchr/testify/assert.
You can do one of three things. The first is to use Examples.
The package also runs and verifies example code. Example functions may include a concluding line comment that begins with "Output:" and is compared with the standard output of the function when the tests are run. (The comparison ignores leading and trailing space.) These are examples of an example:
func ExampleHello() {
fmt.Println("hello")
// Output: hello
}
The second (and more appropriate, IMO) is to use fake functions for your IO. In your code you do:
var myPrint = fmt.Print
func (t *Thing) print(min_verbosity int, message string) {
if t.verbosity >= minv {
myPrint(message) // N.B.
}
}
And in your tests:
func init() {
myPrint = fakePrint // fakePrint records everything it's supposed to print.
}
func Test...
The third is to use fmt.Fprintf with an io.Writer that is os.Stdout in production code, but bytes.Buffer in tests.
You could consider adding a return statement to your function to return the string that is actually printed out.
func (t *Thing) print(min_verbosity int, message string) string {
if t.verbosity >= minv {
fmt.Print(message)
return message
}
return ""
}
Now, your test could just check the returned string against an expected string (rather than the print out). Maybe a bit more in-line with Test Driven Development (TDD).
And, in your production code, nothing would need to change, since you don't have to assign the return value of a function if you don't need it.
Consider the following Go snippet:
func sheep() (int, bool) {
return 1, true
}
func main() {
if dolly, ok := sheep() {
//do something
}
}
As I read on 'Effective Go' this is called the 'comma ok' idiom. As far as I can tell this is used to distinguish from a 'found' and 'not found' thing.
The same can be achieved via:
type Sheep struct {}
func sheep() *Sheep {
return &Sheep{}
}
func main() {
if dolly := sheep(); dolly != nil {
//do something
}
}
The latter example seems to fulfill the same purpose, perhaps even nicer. With the 'comma ok' example the assignment is only valid in the if block.
Perhaps I'm missing some considerations. Which pattern is preferred? And why?
A brief example: http://play.golang.org/p/ATxvle38iE
In Go, a nil value may be a perfectly good value. For example a nil slice works (almost) like an empty slice, and the same may be true for user-defined pointer receivers.
For this reason, the comma-ok or comma-error idiom is usually preferred because it makes it obvious that the caller of the function needs to treat the error case (or the not-ok) case explicitly.
So, these are idiomatic when the Sheep return value may not be valid:
func sheep() (*Sheep, bool) {...}
func sheep() (s *Sheep, ok bool) {...} // Adding names to make it clearer
func sheep() (*Sheep, error) {...}
And this is idiomatic only when the return value is always valid:
func sheep() *Sheep {...}
This is an area where Go is different from other languages, where a nil return value may be used to signal an error. The Go idioms of comma-ok and comma-error neatly work around the "billion-dollar mistake" of nil pointers by making code that doesn't deal with invalid return values look wrong. If you write idiomatic code, you can immediately see when errors are being ignored: for example the assignment to s2 here immediately jumps out as suspicious:
s1 := sheep()
s2, _ := sheep()
Both are acceptable, plus you missed the most common idiom; Returning value, error.
The "comma ok" idiom as referenced in "Effective Go" is typically reserved for the builtin operations, like reading from a map or channel, and for type assertions.
I would use it if you need to return a value where a pointer would be unnecessary, inconvenient, or where nil is a valid value; but depending on the situation value, error could be just as good.