Why would you use fmt.Sprint? - go

I really don't understand the benefit of using fmt.Sprint compared to add strings together with +. Here is an example of both in use:
func main() {
myString := fmt.Sprint("Hello", "world")
fmt.Println(myString)
}
and
func main() {
myString := "Hello " + "World"
fmt.Println(myString)
}
What is the differences and benefits of each?

In your example there are no real differences as you are Sprintf to simply concaternate strings. That is indeed something which can be solved more easily by using the '+' operator.
Take the following example, where you want to print a clear error message like "Product with ID '42' could not be found.". How does that look with your bottom approach?
productID := 42;
myString := "Product with ID '" + productID + "' could not be found."
This would give an error (mismatched types string and int), because Go does not have support for concatenate different types together.
So you would have to transform the type to a string first.
productID := 42
myString := "Product with ID '" + strconv.Itoa(productID) + "' could not be found."
And, this you would have to do for every single data type other than strings.
The fmt package in Go and similar formatting packages in almost any other language solve this by helping you with the conversions and keeping your strings clear of mass '+' operators.
Here is how the example would look like using fmt
product := 42
myString := fmt.Sprintf("Product with ID '%d' could not be found.", product)
Here %d is the formatting verb for 'print the argument as a number'. See https://golang.org/pkg/fmt/#hdr-Printing the various other ways of printing other types.
Compared to concatenating fmt allows you to write your strings in a clear way, separating the template/text from the variables. And, it simplifies printing data types other than strings a lot.

fmt.Sprint is good for concatenation different types of its parameters as it uses reflection under the hood. So, if you need to concat strings - use "+", it's much faster, but if you need to contact number and your profit fmt.Sprint just like that:
message := fmt.Sprint(500, "internal server error")

If you call a function with concatenated string as argument, you will have to evaluate argument prior to call. Then if function chooses not to act on argument (think logging when log level is lower then needed for printing), you already incurred the overhead of concatenation.
Very similar to your example, in one case you do concatenation and in other not.
With high volume of those operations it may become noticeable. Again, logging is a good example.
In specific case of Sprint, it is not that relevant of course, but perhaps it's good to be consistent?

Most of the arguments have already been written, exclude one. Localization with Sprintf is much easier and has better defined roles between programmer and localizator (someone who speaks foreign language). Of course not each app really needs that. Let's choose:
s := fmt.Sprintf(t('%s is %d and comes from %s'), name, age, place)
or
s := name + t(' is ') + strconv.Itoa(age) + t(' and comes from ') + place
Translation of fragments of text is confusing. Also sprintf allows you formatting number etc

Like #Erwin (accepted answer) said, "mismatched types" is a problem with concatenation and using "strconv" seems overly complicated.
var unixTime = time.Now().Unix()
fmt.Println("This doesn't work: "+ string(unixTime))
fmt.Println("Unix timestamp, base 10: "+ strconv.FormatInt(unixTime, 10))
fmt.Println("Unix timestamp, Itoa: "+ strconv.Itoa(int(unixTime)))
// This looks cleaner, in my opinion...
fmt.Println("Unix timestamp, Sprint: "+ fmt.Sprint(unixTime))
Since web development usually involves concatenation of long strings that aren't going to stdout, I see Sprint as a useful tool.

Related

How to convert global enum values to string in Godot?

The "GlobalScope" class defines many fundamental enums like the Error enum.
I'm trying to produce meaningful logs when an error occurs. However printing a value of type Error only prints the integer, which is not very helpful.
The Godot documentation on enums indicates that looking up the value should work in a dictionary like fashion. However, trying to access Error[error_value] errors with:
The identifier "Error" isn't declared in the current scope.
How can I convert such enum values to string?
In the documentation you referenced, it explains that enums basically just create a bunch of constants:
enum {TILE_BRICK, TILE_FLOOR, TILE_SPIKE, TILE_TELEPORT}
# Is the same as:
const TILE_BRICK = 0
const TILE_FLOOR = 1
const TILE_SPIKE = 2
const TILE_TELEPORT = 3
However, the names of the identifiers of these constants only exist to make it easier for humans to read the code. They are replaced on runtime with something the machine can use, and are inaccessible later. If I want to print an identifier's name, I have to do so manually:
# Manually print TILE_FLOOR's name as a string, then its value.
print("The value of TILE_FLOOR is ", TILE_FLOOR)
So if your goal is to have descriptive error output, you should do so in a similar way, perhaps like so:
if unexpected_bug_found:
# Manually print the error description, then actually return the value.
print("ERR_BUG: There was a unexpected bug!")
return ERR_BUG
Now the relationship with dictionaries is that dictionaries can be made to act like enumerations, not the other way around. Enumerations are limited to be a list of identifiers with integer assignments, which dictionaries can do too. But they can also do other cool things, like have identifiers that are strings, which I believe you may have been thinking of:
const MyDict = {
NORMAL_KEY = 0,
'STRING_KEY' : 1, # uses a colon instead of equals sign
}
func _ready():
print("MyDict.NORMAL_KEY is ", MyDict.NORMAL_KEY) # valid
print("MyDict.STRING_KEY is ", MyDict.STRING_KEY) # valid
print("MyDict[NORMAL_KEY] is ", MyDict[NORMAL_KEY]) # INVALID
print("MyDict['STRING_KEY'] is ", MyDict['STRING_KEY']) # valid
# Dictionary['KEY'] only works if the key is a string.
This is useful in its own way, but even in this scenario, we assume to already have the string matching the identifier name explicitly in hand, meaning we may as well print that string manually as in the first example.
The naive approach I done for me, in a Singleton (in fact in a file that contain a lot of static funcs, referenced by a class_name)
static func get_error(global_error_constant:int) -> String:
var info := Engine.get_version_info()
var version := "%s.%s" % [info.major, info.minor]
var default := ["OK","FAILED","ERR_UNAVAILABLE","ERR_UNCONFIGURED","ERR_UNAUTHORIZED","ERR_PARAMETER_RANGE_ERROR","ERR_OUT_OF_MEMORY","ERR_FILE_NOT_FOUND","ERR_FILE_BAD_DRIVE","ERR_FILE_BAD_PATH","ERR_FILE_NO_PERMISSION","ERR_FILE_ALREADY_IN_USE","ERR_FILE_CANT_OPEN","ERR_FILE_CANT_WRITE","ERR_FILE_CANT_READ","ERR_FILE_UNRECOGNIZED","ERR_FILE_CORRUPT","ERR_FILE_MISSING_DEPENDENCIES","ERR_FILE_EOF","ERR_CANT_OPEN","ERR_CANT_CREATE","ERR_QUERY_FAILED","ERR_ALREADY_IN_USE","ERR_LOCKED","ERR_TIMEOUT","ERR_CANT_CONNECT","ERR_CANT_RESOLVE","ERR_CONNECTION_ERROR","ERR_CANT_ACQUIRE_RESOURCE","ERR_CANT_FORK","ERR_INVALID_DATA","ERR_INVALID_PARAMETER","ERR_ALREADY_EXISTS","ERR_DOES_NOT_EXIST","ERR_DATABASE_CANT_READ","ERR_DATABASE_CANT_WRITE","ERR_COMPILATION_FAILED","ERR_METHOD_NOT_FOUND","ERR_LINK_FAILED","ERR_SCRIPT_FAILED","ERR_CYCLIC_LINK","ERR_INVALID_DECLARATION","ERR_DUPLICATE_SYMBOL","ERR_PARSE_ERROR","ERR_BUSY","ERR_SKIP","ERR_HELP","ERR_BUG","ERR_PRINTER_ON_FIR"]
match version:
"3.4":
return default[global_error_constant]
# Regexp to use on #GlobalScope documentation
# \s+=\s+.+ replace by nothing
# (\w+)\s+ replace by "$1", (with quotes and comma)
printerr("you must check and add %s version in get_error()" % version)
return default[global_error_constant]
So print(MyClass.get_error(err)), or assert(!err, MyClass.get_error(err)) is handy
For non globals I made this, though it was not your question, it is highly related.
It would be useful to be able to access to #GlobalScope and #GDScript, maybe due a memory cost ?
static func get_enum_flags(_class:String, _enum:String, flags:int) -> PoolStringArray:
var ret := PoolStringArray()
var enum_flags := ClassDB.class_get_enum_constants(_class, _enum)
for i in enum_flags.size():
if (1 << i) & flags:
ret.append(enum_flags[i])
return ret
static func get_constant_or_enum(_class:String, number:int, _enum:="") -> String:
if _enum:
return ClassDB.class_get_enum_constants(_class, _enum)[number]
return ClassDB.class_get_integer_constant_list(_class)[number]

How to split a string but keep delimiters as separate elements

I have several strings that include various symbols like the following two examples:
z=y+x
#symbol
and I want to split the strings such that I have the resulting slices:
[z = y + x]
[# symbol]
A few things I've looked at and tried:
I've looked at this question but it seems as though golang doesn't support lookarounds.
I know this solution exists using strings.SplitAfter, but I'm looking to have the delimiters as separate elements.
I tried replacing the symbol (e.g. "+") with some variant (e.g. "~+~") and doing a split on the surrounding characters (e.g. "~"), but this solution is far from elegant and runs into problems if I need to do a conditional replacement depending on the symbol (which golang doesn't seem to support either).
Perhaps I've misunderstood some of the previous question and their respective solutions.
I used a modified version of Go's strings.Split implementation https://golang.org/src/strings/strings.go?s=7505:7539#L245
func Test(te *testing.T) {
t := tester.New(te)
t.Assert().Equal(splitCharsInclusive("z=y+x", "=+"), []string{"z", "=", "y", "+", "x"})
t.Assert().Equal(splitCharsInclusive("#symbol", "#"), []string{"", "#", "symbol"})
}
func splitCharsInclusive(s, chars string) (out []string) {
for {
m := strings.IndexAny(s, chars)
if m < 0 {
break
}
out = append(out, s[:m], s[m:m+1])
s = s[m+1:]
}
out = append(out, s)
return
}
This is limited to single characters to split on. And passing something like splitCharsInclusive("(z)(y)(x)", "()") might not get you the output you want, as you'd get a few empty strings in the response. But hopefully this is a good starting point for the modifications you need.
Also, Go's version that I've linked calculates the length of the output array in advance, this is a nice optimization that I've decided to omit, but would likely be good to add back.

Golang: optimal way of typing associative slices?

I'm parsing loads of HTTP logs pursing a goal tell how many requests each IP address generated.
The first thing I did is:
var hits = make(map[string]uint)
// so I could populate it with
hits[ipAddr]++
However, I would like to make it "typed", so that it would be immediately clear that hits[string]uint uses an IP address as a string identifier. I thought, well maybe a struct can help me:
type Hit struct {
IP string
Count uint
}
But that way (I think) I'm loosing the performance, because now I how to really look for specific Hit to increment it's count. I tolerate that I could be paranoid here, and could simple go for the loop:
var hits = make([]Hit)
// TrackHit just damn tracks it
func TrackHit(ip string) {
for hit, _ := range hits {
if hit.IP == ip {
hit.Count++
return
}
}
append(hits, Hit{
IP: ip,
Count: 0,
})
}
But that just looks ... suboptimal. I think everything that could be written in 1 line makes you shine as professional, and when 1 line turns into 13, I tend to feel "whaaa? Doing something wrong here, mom?"
Any typed one-liners here in Go?
Thanks
As Uvelichitel pointed out, you can use a typed string:
type IP string
var hits = make(map[IP]uint)
hits[IP("127.0.0.1")]++
Or you could use the existing stdlib IP type:
var hits = make(map[net.IP]uint)
hits[net.ParseIP("127.0.0.1")]++
Either would make it clear that you're referring to IPs, without the overhead introduced by looping over a slice of structs for every increment. The latter has the advantage of giving you full stdlib support for any other IP manipulation you need to do, and a more compact representation (4 bytes for IPv4 addresses instead of a 7-15 character UTF-8 string), at the cost of parsing the strings. Which one is better will depend on your specific use case.

How to concatenate strings and ints natively?

The shortest (cleanest) way I have found so far to concatenate strings with ints in Go is the following:
"blahblah" + strconv.Itoa(42) + "something"
(importing package strconv)
Since one motto of the Go language is to be simple, I was surprised to have to import a package to do this.
Hence, my question: is there any native (thus cleaner) way of doing this?
Obviously, I tried the following syntax, which does not even compile:
"blahblah" + 42 + "something"
I also tried that one, which is in fact a cast of the value, which is not what I want (it converts the value to the corresponding unicode character, which would be * for the value 42):
"blahblah" + string(42) + "something"
This'll do the trick (it uses reflection though):
str := fmt.Sprintf("blah %d blah", 42)
You can use different print commands to do what you want. It's not a one liner, but it works.
package main
import "fmt"
func main() {
fmt.Print("blahblah")
fmt.Print(42)
fmt.Print("something")
}
Playground

What is "A Tour of Go" trying to say? [closed]

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 10 years ago.
There are a few points in the tutorial that sort of leave you on your own without a clue or link if you're not in the know I guess. So I'm sorry about the length of these:
http://tour.golang.org/#15
Try printing needInt(Big) too
I'm guessing ints are allowed less bits than constants?
http://tour.golang.org/#21
the { } are required.
(Sound familiar?)
Which language is alluded to?
http://tour.golang.org/#25
(And a type declaration does what you'd expect.)
Why do we need the word type and the word struct? What was I supposed to expect?
http://tour.golang.org/#28
Why implicit zeroes in the constructor? This sounds like a dangerous design choice by Go. Is there a PEP or anything beyond http://golang.org/doc/go_faq.html on this?
http://tour.golang.org/#30
Make? Are there constructors? What's the difference between new and make?
http://tour.golang.org/#33
Where did delete come from? I didn't import it.
http://tour.golang.org/#36
What's the %v formatter stand for? Value?
http://tour.golang.org/#47
panic: runtime error: index out of range
goroutine 1 [running]:
tour/pic.Show(0x400c00, 0x40ca61)
go/src/pkg/tour/pic/pic.go:24 +0xd4
main.main()
/tmpfs/gosandbox-15c0e483_5433f2dc_ff6f028f_248fd0a7_d7c2d35b/prog.go:14 +0x25
I guess I broke go somehow....
package main
import "tour/pic"
func Pic(dx, dy int) [][]uint8 {
image := make([][]uint8, 10)
for i := range image {
image[i] = make([]uint8, 10)
}
return image
}
func main() {
pic.Show(Pic)
}
http://tour.golang.org/#59
I return error values when a function fails? I have to qualify every single function call with an error check? The flow of the program is uninterrupted when I write crazy code? E.g. Copy(only_backup, elsewhere);Delete(only_backup) and Copy fails....
Why would they design it like that?
#15:
I'm guessing int's are allowed less bits than constants?
Yes, exactly. According to the spec, "numeric constants represent values of arbitrary precision and do not overflow", whereas type int has either 32 or 64 bits.
#21:
Which language is alluded to?
None; it's alluding to #16, which says the same thing, in the same words, about for-loops.
#25 :
a type declaration does what you'd expect is a little unfortunate, I agree (as it assumes too much on what a reader could expect...) but it means you're defining a struct (with the struct keyword) and binding the type name "Vertex" to it, with the type Vertex part (see http://golang.org/ref/spec#Type_declarations)
#28:
the fact that uninitialized structs are zeroed is really really useful in many cases (many standard structs like buffers use it also)
It's not implicit in the contructor only. Look at this
var i int; fmt.Println(i)
This prints out 0. This is similar to something like java where primitive types have an implicit default value. booleans are false, integers are zero, etc. The spec on zero values.
#30:
new allocates memory and returns a pointer to it, while make is a special function used only for Slices, maps and channels.
See http://golang.org/doc/effective_go.html#allocation_new for a more in-depth explanation of make vs new
#33:
delete, like append or copy is one of the basic operators of the language. See the full list of them at: http://golang.org/ref/spec#Predeclared_identifiers
#36:
Yes, %v stands for "value". See http://golang.org/pkg/fmt/
#47:
try with this:
func Pic(dx, dy int) [][]uint8 {
image := make([][]uint8, dy) // dy, not 10
for x := range image {
image[x] = make([]uint8, dx) // dx, not 10
for y := range image[x] {
image[x][y] = uint8(x*y) //let's try one of the mentioned
// "interesting functions"
}
}
return image
}
#59:
The language's design and conventions encourage you to explicitly
check for errors where they occur (as distinct from the convention in
other languages of throwing exceptions and sometimes catching them).
In some cases this makes Go code verbose, but fortunately there are
some techniques you can use to minimize repetitive error handling.
(quoted from Error handling and Go )
I'm guessing int's are allowed less bits than constants?
yes, Numeric constants are high-precision values. An int in any language doesn't have near the precision of other numeric types.
Which language is alluded to?
No clue but it is backwards from C and Java where ( ) is required and { } is optional.
Why do we need the word type and the word struct? What was I supposed to expect?
If you're familiar with C, then it does what you'd expect.
Why implicit zeroes in the constructor?
It's not implicit in the contructor only. Look at this
var i int
fmt.Println(i)
This prints out 0. This is similar to something like java where primitive types have an implicit default value. booleans are false, integers are zero, etc.
Make? Are there constructors? What's the difference between new and make?
make accepts additional parameters for initializing the size of an array, slice, or map. new on the other hand just returns a pointer to a type.
type Data struct {}
// both d1 and d2 are pointers
d1 := new(Data)
d2 := &Data{}
As for are there constructors?, only if you make and reference them. This how one normally implements a constructor in Go.
type Data struct {}
func NewData() *Data {
return new(Data)
}
What's the %v formatter stand for? Value?
Yep
I return error values when a function fails? ... Why would they design it like that?
I felt the same way at first. My opinion has changed though. You can ignore errors from the std library if you like and not bother with it yourself, but once I had a handle on it, I personally find I have better (and more readable) error checking.
What I can say is when I was doing it wrong, it felt like repetitive error handling that felt unnecessary. When I finally started doing it right... well, what I just said above.

Resources