If I have something like this
Case 1:
if str, err := m.something(); err != nil {
return err
}
fmt.Println(str) //str is undefined variable
Case 2:
str, err := m.something();
fmt.Println(str) //str is ok
My question is why does the scope of the variable str change when its used in a format like this
if str, err := m.something(); err != nil {
return err
//str scope ends
}
Because if statements (and for, and switch) are implicit blocks, according to the language spec, and := is for both declaration and assignment. If you want str to be available after the if, you could declare the variables first, and then assign to them in the if statement:
var s string
var err error
if str, err = m.something(); err != nil
// ...
Related
I'm trying to retrieve cursor result of a PL/SQL function in golang with godror.
https://github.com/godror/godror
Using cursors returned by stored procedures
Use ExecContext and an interface{} or a database/sql/driver.Rows as the sql.Out destination, then either use the driver.Rows interface, or transform it into a regular *sql.Rows with godror.WrapRows, or (since Go 1.12) just Scan into *sql.Rows.
db, err := sql.Open("godror", "api/user#localhost/DEV")
if err != nil {
fmt.Println(err)
panic(err)
}
defer db.Close()
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
const query = `
DECLARE
BEGIN
:cursor := pkg_api.my_func(pstr_logn_nm => 'my_name');
END;
`
stmt, err := db.PrepareContext(ctx, query)
if err != nil {
fmt.Println(err)
panic(err)
}
var rows driver.Rows
_, err = stmt.ExecContext(ctx, sql.Out{Dest: &rows})
if err != nil {
fmt.Println(err)
}
var r []driver.Value
err = rows.Next(r)
if err != nil {
fmt.Println(err) // column count mismatch: we have 10 columns, but given 0 destination
}
defer rows.Close()
fmt.Println(rows.Columns()) // [COL_1 COL_2 COL_3 COL_4 COL_5 COL_6 COL_7 COL_8 COL_9 COL_10]
I have an error: column count mismatch: we have 10 columns, but given 0 destination
To my point of view it's because I have to define an interface with some columns.
If I try to change driver.Rows by sql.Rows I have this error:
arg: unknown type sql.Rows
If I try to create an interface I:
type I interface {
NM() string
}
var r []I
err = rows.Next(r)
I have this error: cannot use r (type []I) as type []driver.Value in argument to rows.Next
I'm also new in Golang, if someone has an idea :)
Thanks a lot!
This solved my problem!
r := make([]driver.Value, len(rows.Columns()))
err = rows.Next(r)
if err != nil {
fmt.Println(err) // column count mismatch: we have 10 columns, but given 0 destination
}
defer rows.Close()
https://github.com/godror/godror/issues/62
In Go, one often sees the following idiom:
func CopyFile(dstName, srcName string) (written int64, err error) {
src, err := os.Open(srcName)
if err != nil {
return
}
defer src.Close()
dst, err := os.Create(dstName)
if err != nil {
return
}
defer dst.Close()
return io.Copy(dst, src)
}
Is there any reason why the defer statement comes after the error check? My guess is that this is done in order to avoid dereferencing nil values in case err was not nil.
If the file Open or Create fails then you don't have a valid *File to close. The problem wouldn't be a nil value for *File as Close() will check for nil and simply return immediately in that case - the problem might be if the *File value is non-nil but invalid. Since documentation for os.Open() doesn't explicitly state that a failed call to Open() returns a nil value for *File you can't rely that all underlying implementations of it do in fact return a nil value or will always return a nil value..
I'm confusing about the reassignment of the err variable for errors in Go.
For example, I tend to be doing this:
err1 := Something()
checkErr(err1)
str, err2 := SomethingElse()
checkErr(err2)
err3 := SomethingAgain()
checkErr(err3)
But I'm always losing track of this and have millions of useless err variables floating around that I don't need, and it makes the code messy and confusing.
But then if I do this:
err := Something()
checkErr(err)
str, err := SomethingElse()
checkErr(err)
err := SomethingAgain()
checkErr(err)
...it gets angry and says err is already assigned.
But if I do this:
var err error
err = Something()
checkErr(err)
str, err = SomethingElse()
checkErr(err)
err = SomethingAgain()
checkErr(err)
...it doesn't work because str needs to be assigned with :=
Am I missing something?
you're almost there... at the left side of := there needs to be at least one newly create variable. But if you don't declare err in advance, the compiler tries to create it on each instance of :=, which is why you get the first error. so this would work, for example:
package main
import "fmt"
func foo() (string, error) {
return "Bar", nil
}
func main() {
var err error
s1, err := foo()
s2, err := foo()
fmt.Println(s1,s2,err)
}
or in your case:
//we declare it first
var err error
//this is a normal assignment
err = Something()
checkErr(err)
// here, the compiler knows that only str is a newly declared variable
str, err := SomethingElse()
checkErr(err)
// and again...
err = SomethingAgain()
checkErr(err)
i am trying to write to to a file. i read the whole content of the file and now i want to change the content of the file based on some word that i have got from the file. but when i check, the content of the file, it is still the same and it has not change. this is what i used
if strings.Contains(string(read), sam) {
fmt.Println("this file contain that word")
temp := strings.ToUpper(sam)
fmt.Println(temp)
err := ioutil.WriteFile(fi.Name(), []byte(temp), 0644)
} else {
fmt.Println(" the word is not in the file")
}
Considering that your call to ioutil.WriteFile() is consistent with what is used in "Go by Example: Writing Files", this should work.
But that Go by example article check the err just after the write call.
You check the err outside the scope of your test:
if matched {
read, err := ioutil.ReadFile(path)
//fmt.Println(string(read))
fmt.Println(" This is the name of the file", fi.Name())
if strings.Contains(string(read), sam) {
fmt.Println("this file contain that word")
Value := strings.ToUpper(sam)
fmt.Println(Value)
err = ioutil.WriteFile(fi.Name(), []byte(Value), 0644)
} else {
fmt.Println(" the word is not in the file")
}
check(err) <===== too late
}
The err you are testing is the one you got when reading the file (ioutil.ReadFile), because of blocks and scope.
You need to check the error right after the Write call
err = ioutil.WriteFile(fi.Name(), []byte(Value), 0644)
check(err) <===== too late
Since WriteFile overwrite the all file, you could strings.Replace() to replace your word by its upper case equivalent:
r := string(read)
r = strings.Replace(r, sam, strings.ToUpper(sam), -1)
err := ioutil.WriteFile(fi.Name(), []byte(r), 0644)
For a replace which is case insensitive, use a regexp as in "How do I do a case insensitive regular expression in Go?".
The, use func (*Regexp) ReplaceAllString:
re := regexp.MustCompile("(?i)\\b"+sam+"\\b")
r = re.ReplaceAllString(r, strings.ToUpper(sam))
err := ioutil.WriteFile(fi.Name(), []byte(r), 0644)
Note the \b: word boundary to find the any word starting and ending with sam content (instead of finding substrings containing sam content).
If you want to replace substrings, simply drop the \b:
re := regexp.MustCompile("(?i)"+sam)
It's not clear what you want to do. My best guess is something like this:
package main
import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"os"
)
func UpdateWord(filename string, data, word []byte) (int, error) {
n := 0
f, err := os.OpenFile(filename, os.O_WRONLY, 0644)
if err != nil {
return n, err
}
uWord := bytes.ToUpper(word)
if len(word) < len(uWord) {
err := errors.New("Upper case longer than lower case:" + string(word))
return n, err
}
if len(word) > len(uWord) {
uWord = append(uWord, bytes.Repeat([]byte{' '}, len(word))...)[:len(word)]
}
off := int64(0)
for {
i := bytes.Index(data[off:], word)
if i < 0 {
break
}
off += int64(i)
_, err = f.WriteAt(uWord, off)
if err != nil {
return n, err
}
n++
off += int64(len(word))
}
f.Close()
if err != nil {
return n, err
}
return n, nil
}
func main() {
// Test file
filename := `ltoucase.txt`
// Create test file
lcase := []byte(`update a bc def ghij update klmno pqrstu update vwxyz update`)
perm := os.FileMode(0644)
err := ioutil.WriteFile(filename, lcase, perm)
if err != nil {
fmt.Println(err)
return
}
// Read test file
data, err := ioutil.ReadFile(filename)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(data))
// Update word in test file
word := []byte("update")
n, err := UpdateWord(filename, data, word)
if err != nil {
fmt.Println(n, err)
return
}
fmt.Println(filename, string(word), n)
data, err = ioutil.ReadFile(filename)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(data))
}
Output:
update a bc def ghij update klmno pqrstu update vwxyz update
ltoucase.txt update 4
UPDATE a bc def ghij UPDATE klmno pqrstu UPDATE vwxyz UPDATE
I'm looking at the code examples sql.query and i'm a bit confused by the way the variables are initialized. As far as I understand the var keyword initialize the variable but if you already have a such variable it's better to 'reuse' it instead to reinitialize it. I'm aware that I might have misunderstood the golang specs so I hope this question would help me (and perhaps other folks) get it right.
rows, err := db.Query("SELECT name FROM users WHERE age=?", age)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var name string
if err := rows.Scan(&name); err != nil {
log.Fatal(err)
}
fmt.Printf("%s is %d\n", name, age)
}
if err := rows.Err(); err != nil {
log.Fatal(err)
}
Why is the "name" variable initialized within the loop and not outside the loop ? (see below). Isn't it less performant to reinitialize it on each loop ?
//how I would do this
rows, err := db.Query("SELECT name FROM users WHERE age=?", age)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
var name string //outside the loop
for rows.Next() {
if err := rows.Scan(&name); err != nil {
log.Fatal(err)
}
fmt.Printf("%s is %d\n", name, age)
}
if err := rows.Err(); err != nil {
log.Fatal(err)
}
or even better use a pointer
rows, err := db.Query("SELECT name FROM users WHERE age=?", age)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
name := new(string) //pointer outside the loop
for rows.Next() {
if err := rows.Scan(name); err != nil {
log.Fatal(err)
}
fmt.Printf("%s is %d\n", name, age)
}
if err := rows.Err(); err != nil {
log.Fatal(err)
}
Unless you have determined that the allocation is a performance bottleneck, I wouldn't consider such a premature optimisation. After all, it might not even make a difference, so it is best to err on the side of readability/maintainability.
In general, I'd suggest using the smallest scope for your variables that makes sense. If they are stack allocated, then they will be quite cheap -- assuming space is available, it probably just involves initialising the variable to zero or its initial value. Stack allocated variables scoped within a loop will probably end up with the same memory location each time through the loop too, so there isn't much to be gained from moving them out.
With that said, it isn't always obvious when a variable will be allocated on the stack. If the compiler decides that the pointer passed to row.Scan could possibly be retained past the function call (that is, it escapes), then name will be allocated on the heap even though it has been defined with var.
Similarly if the escape analysis determines that the variable doesn't escape, the version that creates the string variable with new may decide to place it on the stack.