Why Do method of Conn interface is inlined - go

I created a redigo application with following code
conn, err := redis.Dial("tcp", "localhost:6379")
if err != nil {
log.Fatal(err)
}
defer conn.Close()
_, err = conn.Do("HMSET", "album:1", "title", "Electric Ladyland", "artist", "Jimi Hendrix", "price", 4.95, "likes", 8)
if err != nil {
log.Fatal(err)
}
created a binary from above code and took the objdump of binary. I found that there is no definition of conn.Do in objdump as it became inline.
In redigo code, I changed the following function in conn.go but it did not work
//go:noinline
func (c *conn) Do(cmd string, args ...interface{}) (interface{}, error) {
return c.DoWithTimeout(c.readTimeout, cmd, args...)
}
Can someone suggest how make Do method noline?

As illustrated here, a //go:noinline pragma before a function declaration should have disabled inlining of that specific function.
But: if you are modifying github.com/gomodule/redigo/redis/conn.go, used in your project as a library (go get github.com/gomodule/redigo, added to your project go.mod/go.sum), that would not work.
Go would still compile the official cached module dependency for github.com/gomodule/redigo, not your local modification.
Hence, your //go:noinline is ignored.
You would need to fork the project, and go get that fork, and use a replace directive, as illustrated in "Go Module’s Replace" from nwillc:
replace github.com/pgavlin/femto => github.com/nwillc/femto v0.0.0-20201217031030-474e70183a86
That "replaced" the github.com/pgavlin/femto with my fork github.com/nwillc/femto.
And finally, back in snipgo I tried again:
go get -u github.com/nwillc/snipgo
Only then, any modification done to the source of (your forked) redigo repository would "stick" and be compiled.

Related

"err declared but not used" with multiple errors

If I have a program like this:
package main
import "strconv"
func main() {
a, err := strconv.Atoi("100")
println(a)
}
I get this result, as expected:
.\file.go:5:7: err declared but not used
However this program:
package main
import "strconv"
func main() {
a, err := strconv.Atoi("100")
if err != nil {
panic(err)
}
b, err := strconv.Atoi("100")
println(a, b)
}
Compiles without error, even though I never checked the second err value. Why does this happen? Also, can I change some option, so that these mistakes result in compile time errors or warnings?
This is because in the second case you are re-using an existing err variable, so it is being used. Despite the := instantiate & assign operator, a new err variable is not instantiated.
If you named the errors differently, such as this:
func main() {
a, err := strconv.Atoi("100")
if err != nil {
panic(err)
}
b, err2 := strconv.Atoi("100")
println(a, b)
}
Then you would see a compile error for both cases.
If you don't want to change your code but also still want to be notified of this issue, you will need to rely on a go linter instead of the go compiler. Go has a very robust ecosystem of linters so I won't recommend one in particular, but at my organization I would see an error like this from our linter if I were to write such code:
scratch/test.go:10:2: ineffectual assignment to err (ineffassign)
b, err := strconv.Atoi("100")

Passing a pointer to bufio.Scanner()

Lest I provide an XY problem, my goal is to share a memory-mapped file between multiple goroutines as recommended. Each goroutine needs to iterate over the file line by line so I had hoped to store the complete contents in memory first to speed things up.
The method I tried is passing a pointer to a bufio.Scanner, but that is not working. I thought it might be related to needing to set the seek position back to the beginning of the file but it is not even working the very first time and I can find no such parameter in the documentation. My attempt was to create this function then pass the result by reference to the function I intend to run in a goroutine (for right now, I am not using goroutines just to make sure this works outright, which it does not).
Here is a MWE:
// ... package declaration; imports; yada yada
func main() {
// ... validate path to file stored in filePath variable
filePath := "/path/to/file.txt"
// get word list scanner to be shared between goroutines
scanner := getScannerPtr(&filePath)
// pass to function (no goroutine for now, I try to solve one problem at a time)
myfunc(scanner)
}
func getScannerPtr(filePath *string) *bufio.Scanner {
f, err := os.Open(*filePath)
if err != nil {
fmt.Fprint(os.Stderr, "Error opening file\n")
panic(err)
}
defer f.Close()
scanner := bufio.NewScanner(f)
scanner.Split(bufio.ScanLines)
return scanner
}
func myfunc(scanner *bufio.Scanner) {
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
// ... do something with line
}
}
I'm not receiving any errors, it just is not iterating over the file when I call Scan() so it never makes it inside that block to do anything with each line of the file. Keep in mind I am not even using concurrency yet, that is just my eventual goal which I want to point out in case that impacts the method I need to take.
Why is Scan() not working?
Is this is a viable approach if I intend to call go myfunc(scanner) in the future?
You're closing the file before you ever use the Scanner:
func getScannerPtr(filePath *string) *bufio.Scanner {
f, err := os.Open(*filePath)
if err != nil {
fmt.Fprint(os.Stderr, "Error opening file\n")
panic(err)
}
defer f.Close() // <--- Here
scanner := bufio.NewScanner(f)
scanner.Split(bufio.ScanLines)
return scanner // <-- File gets closed, then Scanner that tries to read it is returned for further use, which won't work
}
Because Scanner does not expose Close, you'll need to work around this; the quickest is probably to make a simple custom type with a couple of embedded fields:
type FileScanner struct {
io.Closer
*bufio.Scanner
}
func getScannerPtr(filePath *string) *FileScanner {
f, err := os.Open(*filePath)
if err != nil {
fmt.Fprint(os.Stderr, "Error opening file\n")
panic(err)
}
scanner := bufio.NewScanner(f)
return &FileScanner{f, scanner}
}
func myfunc(scanner *FileScanner) {
defer scanner.Close()
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
// ... do something with line
}
}

Can we create context managers in Go like how we have in python

In python, I find the context managers really helpful. I was trying to find the same in Go.
e.g:
with open("filename") as f:
do something here
where open is a context manager in python handling the entry and exit, which implicitly takes care of closing the file opened.
Instead of we explicitly doing like this:
f := os.Open("filename")
//do something here
defer f.Close()
Can this be done in Go as well ? Thanks in advance.
No, you can't, but you can create the same illusion with a little wrapper func:
func WithFile(fname string, fn func(f *os.File) error) error {
f, err := os.Open(fname)
if err != nil {
return err
}
defer f.Close()
return fn(f)
}

Finding imports and dependencies of a go program

The go list -json command run from the command line will tell you the imports and dependencies of a go program ( in json format). Is there a way to get this information from within a go program I.e at runtime, either by running the 'go list' command somehow or another way?
The following code uses the go/build to get the imports for the application in the current working directory.
p, err := build.Default.Import(".", ".", 0)
if err != nil {
// handle error
}
for _, i := range p.Imports {
fmt.Println(i)
}
You can build a list of all dependencies using a simple recursive function.
To get the imports for a specific path, use:
p, err := build.Default.Import(path, ".", 0)
if err != nil {
// handle error
}
for _, i := range p.Imports {
fmt.Println(i)
}
I don't think you can do it without using the go binary since go needs to analyze your source code.
It's pretty easy to do but it must have access to go and your source code at run time. Heres a quick example:
package main
import (
"encoding/json"
"fmt"
"os/exec"
)
func main() {
cmd := exec.Command("go", "list", "-json")
stdout, err := cmd.Output()
if err != nil {
println(err.Error())
return
}
var list GoList
err = json.Unmarshal(stdout, &list)
for _, d := range list.Deps {
fmt.Printf(" - %s\n", d)
}
}
type GoList struct {
Dir string
ImportPath string
Name string
Target string
Stale bool
Root string
GoFiles []string
Imports []string
Deps []string
}
No, I don't think this is possible without the source in a reliable way. Go binaries on different platforms, compiled with different compilers may or may not have (or may not have in the future) these informations compiled in.
But as Go programs are compiled anyway: Why not record this information while you do have access to the source code?

Working in the console via exec.Command

Please help. I have to pass the console commando with a certain number of parameters. There are many.
That is, ideally, should be as follows:
test.go --distr
For example:
test.go --distr mc curl cron
i create function
func chroot_create() {
cmd := exec.Command("urpmi",
"--urpmi-root",
*fldir,
"--no-verify-rpm",
"--nolock",
"--auto",
"--ignoresize",
"--no-suggests",
"basesystem-minimal",
"rpm-build",
"sudo",
"urpmi",
"curl")
if err := cmd.Run(); err != nil {
log.Println(err)
}
}
And catch parameter distr through flag.Parse ()
How do I get rid of
"rpm-build",
"sudo",
"urpmi",
"curl")
That would not be tied to count packets. Please forgive me for stupidity, I'm just starting to learn golang. Especially when there was a problem.
Full code http://pastebin.com/yeuKy8Cc
You are looking for the ... operator.
func lsElements(elems ...string) {
cmd := exec.Command("ls", append([]string{"-l", "-h", "/root"}, elems...)...)
if err := cmd.Run(); err != nil {
log.Println(err)
}
}
You receive as function parameter ...string which is in really a []string, except that when you call the function, you pass the strings separately.
In order to use it, (and it works with any slices), you can "transform" your slice into list of element with ... suffix.
In the case of exec, you could use elem... directly if you had only this. However, has you have fixed parameters as well, you need to build your slice with append and extend it back with ...
Example: http://play.golang.org/p/180roQGL4a

Resources