I am trying to execute a SQL statement in the code below. However, sqlRes is unused and therefore cannot be compiled. I do not need the variable, but I need to declare it because Exec() returns multiple values.
How do I approach this?
stmt, err := db.Prepare("INSERT person SET name=?")
sqlRes, err := stmt.Exec(person.Name)
Replace sqlRes with the blank identifier (_). From the spec:
The blank identifier provides a way to ignore right-hand side values in an assignment:
_ = x // evaluate x but ignore it
x, _ = f() // evaluate f() but ignore second result value
Example:
stmt, err := db.Prepare("INSERT person SET name=?")
_, err = stmt.Exec(person.Name)
Another way to avoid the no new variables error is to wrap the check in an
if block:
if _, err := stmt.Exec(person.Name); err != nil {
panic(err)
}
https://golang.org/ref/spec#If_statements
Related
func CheckKafkaReadPartitions(kafkabroker string, topic string, conf config.Config) bool {
var conn *kafka.Conn
if conf.TlsEnabled {
d := &kafka.Dialer{
TLS: &tls.Config{},
}
conn, err := d.Dial("tcp", kafkabroker)
log.Info("conn is: ", conn)
log.Info("Using TLS connection")
if err != nil {
log.WithError(err).Warn("Kafka broker connection error")
return false
}
defer conn.Close()
} else {
conn, err := kafka.Dial("tcp", kafkabroker)
log.Info("conn is: ", conn)
log.Info("Using Plaintext connection")
if err != nil {
log.WithError(err).Warn("Kafka broker connection error")
return false
}
defer conn.Close()
}
log.Info("conn is: ", conn)
log.Info("Reading Partitions")
partitions, err := conn.ReadPartitions()
// SOME OTHER WORK
}
I noticed that when calling ReadPartitions() method, conn is empty even after affecting values to it either in conn, err := kafka.Dial("tcp", kafkabroker) or conn, err := d.Dial("tcp", kafkabroker)
What am I missing? Is there any way I could take conn var out of that if/else block without emptying its content?
So basically what happens here is a variable shadowing.
Go has variable scopes, you can have a variable in a global scope by defining it outside of a function. Then you'll be able to use this variable anywhere in the same package (or if it is exported anywhere in your code).
Then you have the variables that are defined in a block of code. Similar to var conn *kafka.Conn you can access this variable from everywhere in the block it was defined (and all the sub-blocks).
Think the blocks as the code that is enclosed by the curly brackets {}
This means that the if/else are separate blocks under the func block.
Now what you need to understand is the difference between the = and :=
= is used to assign a value to a variable while := is a shorthand that is used to declare and assign a variable.
So by using conn, err := d.Dial("tcp", kafkabroker) code what you essentially do is declare new variables in the if block and assign the values to them from the returned values of the d.Dial func cal.
There are some cases that you may want to do that. The most common case is when you have a for loop that launches goroutines that uses a variable from an outer block.
I have a situation where if something fails to assign a variable I instead assign it within a conditional statement however Go seems to think this variable is unused.
for index, value := range group.Values {
timestamp, err := time.Parse(time.RFC3339, value.Timestamp)
if err != nil {
strings.ReplaceAll(value.Timestamp, "+0000", "")
timestamp, err := time.Parse(time.RFC3339, value.Timestamp)
if err != nil {
log.Printf("Error parsing timestamp for point %s: (%s) %v", value.Context+"_"+value.ID, value.Timestamp, err)
continue
}
}
event := new(SplunkEvent)
event.Time = timestamp.Unix()
Go believes the variable timestamp inside the conditional statement is unused. Why is that? I have clearly used it directly after the condition.
The nested (inner) timestamp declaration shadows the outer one - so the outer one is never set. So since the inner value is never used, the compilation error is valid.
To fix, replace := declared assignment operator with = to (re)use the outer scope's timestamp (and err) values:
timestamp, err = time.Parse(time.RFC3339, value.Timestamp)
I seem to have the correct usage for using the HDEL command interface, but seem to get 0 records deleted. Am I missing something here?
Here are useful code snippets:
This doesn't work:
keysToDeleteArr []string //this has valid key values from upstream
s.transClient.Do("MULTI")
_, err := s.transClient.Do("HDEL", myhash, keysToDeleteArr)
s.transClient.Do("EXEC")
Gives an output (int64) 0 // # of keys deleted
This does work:
s.transClient.Do("MULTI")
for _, keyToDelete := range keysToDeleteArr {
_, err := s.transClient.Do("HDEL", myhash, keyToDelete)
}
s.transClient.Do("EXEC")
Gives an output (int64) 1 for each HDEL. From the documentation and from static code analysis on the redigo lib, does seem like slices are acceptable parameters for fields
Construct an []interface{} containing the command arguments. Pass the slice of interface{} to the Do method as a variadic argument:
args := make([]interface{}, 0, len(keysToDeleteArr) + 1)
args = append(args, myhash)
for _, v := range keysToDeleteArr {
args = append(args, v)
}
_, err := s.transClient.Do("HDEL", args...)
Use Redigo's Args to execute the code above in a single line:
_, err := s.transClient.Do("HDEL", redis.Args{}.Add(myhash).AddFlat(keysToDeleteArr)...)
I declare some variables (offsetI and limitI) outside of a conditional statement. Inside the conditional statement I am trying to assign them values, then use those values for a query after the conditional statement.
var (
number, size, offset, limit string
offsetI, limitI uint64
)
// Get the string values for number, size, offset, and limit
// ...
if size != "" {
// Parse the number value
numberI, err := strconv.ParseUint(number, 10, 64)
if err != nil {...}
// Parse the size value
limitI, err = strconv.ParseUint(size, 10, 64)
if err != nil {...}
// Calculate the offset
offsetI = numberI * limitI
} else {
// Parse the limit value
limitI, err := strconv.ParseUint(limit, 10, 64) // limitI declared and not used
if err != nil {...}
// Parse the offset value
offsetI, err = strconv.ParseUint(offset, 10, 64)
if err != nil {...}
}
// Make the query using offsetI and limitI
result, err := s.GetAllPaginated(offsetI, limitI)
if err != nil {...}
I am not intending to re-declare the limitI variable in the scope of the else statement, but I need to use the := operator for declaring a new err variable.
The only thing I could come up with was to separately declare another err variable, so I could use a regular assignment statement:
} else {
var err error // New
// Regular assignment statement now
limitI, err = strconv.ParseUint(limit, 10, 64)
if err != nil {...}
I would like to be able to do this without having to declare an additional error variable.
The extra var error is awkward, but it's a common way to address this situation. The spec on scoping says (emphasis mine):
The scope of a constant or variable identifier declared inside a function begins at the end of the ConstSpec or VarSpec (ShortVarDecl for short variable declarations) and ends at the end of the innermost containing block.
So in your case, that short variable declaration is declaring a different limitI scoped to the "innermost containing block." Since it only "lives" until the next closing brace, it isn't used.
In your specific case, an option might be to declare err outside the if/else, since it's used in both inner scopes, so you can use use = instead of := with those functions returning errors. Then there's no "inner limitI" declared and you have no unused variable issue.
"Shadowing" situations like this can also produce unexpected behavior rather than an error. go vet -shadow tries to detect "[v]ariables that may have been unintentionally shadowed" and, different but related, gordonklaus/ineffasign generalizes the "unused variable" check to detect useless assignments even if they weren't declarations.
The only thing I could come up with was to separately declare another err variable
This is being studied in the context of a future Go 2 (2019? 2020).
See "Error Handling — Problem Overview" (Russ Cox - August 27, 2018)
A possible proposed new syntax would avoid having to redeclare err:
Example:
func CopyFile(src, dst string) error {
handle err {
return fmt.Errorf("copy %s %s: %v", src, dst, err)
}
r := check os.Open(src)
defer r.Close()
w := check os.Create(dst)
handle err {
w.Close()
os.Remove(dst) // (only if a check fails)
}
check io.Copy(w, r)
check w.Close()
return nil
}
Note that go vet -shadow is no longer available since Go 1.12 (February 2019):
The experimental -shadow option is no longer available with go vet.
Checking for variable shadowing may now be done using
go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow
go vet -vettool=$(which shadow)
I am a newbee to golang, and I write a program to test io package:
func main() {
readers := []io.Reader{
strings.NewReader("from string reader"),
bytes.NewBufferString("from bytes reader"),
}
reader := io.MultiReader(readers...)
data := make([]byte, 1024)
var err error
//var n int
for err != io.EOF {
n, err := reader.Read(data)
fmt.Printf("%s\n", data[:n])
}
os.Exit(0)
}
The compile error is "err declared and not used". But I think I have used err in for statement. Why does the compiler outputs this error?
The err inside the for is shadowing the err outside the for, and it's not being used (the one inside the for). This happens because you are using the short variable declaration (with the := operator) which declares a new err variable that shadows the one declared outside the for.