Go 1.20 introduces the errors.Join function that can wrap multiple errors. Are there any issues with calling this function and only passing in a single error?
For example, this article recommends against using the defer f.Close() idiom for writable files, because that would silently ignore any error returned by Close. Instead, it suggests using a named return value err to allow the return value of Close to be be propagated - unless doing so would overwrite an earlier error:
defer func() {
cerr := f.Close()
if err == nil {
err = cerr
}
}()
It seems more correct to use errors.Join in this scenario:
defer func() {
cerr := f.Close()
err = errors.Join(err, cerr)
}()
If both err and cerr are non-nil, this will now return both errors. If both are nil, it will return nil.
However, if one is nil and the other non-nil, errors.Join will not just return the non-nil error but an errors.joinError wrapper around it. Could wrapping an error like this cause any problems? Especially if several functions in the call stack use this approach, so a single error could end up within multiple layers of wrapper?
If errors.JoinError has only one non-nil error, that is still a join-error, and errors.As and errors.Is functions work as expected. This is correct no matter the level of nesting of joined errors.
The only potential problem would be if there is code like:
err:=someFunc()
if err==io.EOF {
...
}
then this will fail. This code has to be rewritten to use errors.Is.
I have some Go code that queries a Database using the "database/sql" package. Most of these functions return: result, err or rows, err.
Because of this, you end up with many repetitive blocks like this:
if err != nil {
// Handle Error
}
I've seen "cleaner" code for functions that only return err:
if err := SomeFunc(); err != nil {
// Handle Error
}
I can't do this with these functions because the variable gets trapped in the scope when I need to access it in another query function later on.
Is there a way to clean up this go code?
They're only trapped in scope if they're declared in the if block using :=. If you declare them outside the if, they're in the outer scope:
var err error
var result SomeResultType
if result,err = SomeFunc(); err != nil {
// Handle error
}
// Do something with result (or error if you want)
I have a function which writes/updates a json. But I need to stop the executable, run go build again and re-run the executable to get the json updated in url.
For example, I have a Handler.go file which takes argument from URL as key and runs an if condition and updates the json. So If json value before building the executable is {"Name":"Sneha"} and i pass parameter "Nair" in the url, the json gets updated in the server as {"Name":"Nair"}, but doesnt get updated in the URL. So I have to stop the executable, run go build again and run the executable again to reflect the new json value {"Name":"Nair"} in the URL.
1. Can somebody please suggest an alternative idea ?
2. Can we run go build or go update inside a function?
Help much appreciated.
PS: I have got URL for goagain.go. But am not sure if that matches my requirement.
Handler.go
func handler(w http.ResponseWriter, r *http.Request) {
keys, ok := r.URL.Query()["key"]
if !ok || len(keys) < 1 {
log.Println("Url Param 'key' is missing")
return
}
key := keys[0]
log.Println("Url Param 'key' is: " + string(key))
if key == "java" {
commands := []string{
"Version=`java -version`",
"sed -i -e 's/^\\( *Name: *\\) .*$/ Name:\"Java\",/' Handler.go",
"sed -i -e 's/^\\( *Version: *\\) .*$/ Version:\" '$Version'\",/' Handler.go",
}
exe_cmd(commands)
}
if key == "go" {
commands := []string{
"Version=`go version`",
"sed -i -e 's/^\\( *Name: *\\) .*$/ Name:\"Go\",/' Handler.go",
"sed -i -e 's/^\\( *Version: *\\) .*$/ Version:\" '$Version'\",/' Handler.go",
}
exe_cmd(commands)
}
GetHands := GetHand{
Name:"java",
Version:" 1.7.0_71",
}
if err := json.NewEncoder(w).Encode(GetHands); err != nil {
panic(err)
}
So on running this package, the url shows json value : {"name":"java","version":" 1.7.0_71"}
If I call url : http://localhost:8080/?key=go this Handler.go gets updated to,
GetHands := GetHand{
Name:"go",
Version:" 1.9",
}
If I stop the executable,
run go build again and run executable again the url gets returned as :{"name":"go","version":" 1.9"}
So basically I need dynamic url which on hitting the http:/localhost/?key=go would return go's corresponding value annd htpp://localhost/?key=java would return java's corresponding value. This should be attained without restarting the executable or re-running the go build
Its quite difficult to understand exactly what you want. But I suspect that is essence you simply want to extract the output from a shell command and write it to JSON.
For this there is no need to modify the Handler.go file or do go build. You can simply write the output directly into the GetHand structure.
A basic example is as follows :
package main
import (
"fmt"
"os/exec"
)
var cmds = map[string][]string{
"go": []string{"/usr/local/go/bin/go", "version"},
"java": []string{"java", "-version"},
}
type GetHand struct {
Name string
Version string
}
func handleKey(key string) (*GetHand, error) {
cmd := cmds[key]
if cmd == nil {
return nil, fmt.Errorf("No such key : %v", key)
}
b, err := exec.Command("/usr/local/go/bin/go", "version").Output()
if err != nil {
return nil, err
}
return &GetHand{
Name: key,
Version: string(b),
}, nil
}
func main() {
h, err := handleKey("go")
if err != nil {
fmt.Println(err)
return
}
fmt.Println(h)
h, err = handleKey("java")
if err != nil {
fmt.Println(err)
return
}
fmt.Println(h)
}
Your current code with all those sed tried to modify yor source code. It does not work that way. Some ancient practices (usually using bash) auctually did that kind of staff but it is outdated and problemtic.
As for go, you sure know go is a compiled language, which compiles source code into binaries. In that way, any modification to the source code will not affect binaries, and resulting the process running unaware of that. In that sense, modifying the source code by the process it self is wrong and uneffective. Even if it is possible to use compiler to rebuild binaries, it is going to be both dangerous and inefficient. Do not do it that way.
In your specified case, there is a much better and a much common practice: variable. You sure use it everyday. It would be the simplest to just set the fields of GetHands to needed value. By doing that, your response to each request (what you call URL, but it is wrong) will change because the fields of GetHands change as variable.
I write the code below:
import (
"os/exec"
"regex"
"runtime"
)
var pattern = regexp.MustCompile(`version \"(.+)\"`)
func GetJavaVersion() string {
cmd := exec.Command("Java", "-version")
out, err := cmd.Output()
if err != nil {
panic(err)
}
return string(pattern.FindSubmatch(out)[1])
}
func GetGoVersion() string {
return runtime.Version()[2:]
}
func handler(w http.ResponseWriter, r *http.Request) {
keys, ok := r.URL.Query()["key"]
if !ok || len(keys) < 1 {
log.Println("Url Param 'key' is missing")
return
}
key := keys[0]
log.Println("Url Param 'key' is: " + string(key))
var GetHands GetHand
if key == "java" {
GetHands = GetHand{Name: "java", Version: GetJavaVersion()}
}
if key == "go" {
GetHands = GetHand{Name: "go", Version: GetGoVersion()}
}
if err := json.NewEncoder(w).Encode(GetHands); err != nil {
panic(err)
}
}
So, for go, you can auctually get the version info from package runtime, though the info started with "go". You can simply slice it. And for java, you would have to run a command: The same as your orignal code: java -version. But instead of passing it to sed, you get the output from it by using Command.Output and then use a regexp to find the version information. regexp is a very useful tool to get info from strings. You can read about it here
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
}
}
I have a list of strings which can contain number of elements ranging from 1 to 100,000. I want to verify each string and see if they are stored in a database, which requires call to network.
In order to maximize the efficiency, I want to spawn a go routine for each element.
Goal is to return false if one of the verifications inside the go routine function returns err, and return true if there is no err. So if we find at least one err we can stop since we already know that it is going to return false.
This is the basic idea, and the function below is the structure I've been thinking about using so far. I'd like to know if there is a better way (perhaps using channel?).
for _, id := range userIdList {
go func(id string){
user, err := verifyId(id)
if err != nil {
return err
}
// ...
// few more calls to other APIs for verifications
if err != nil {
return err
}
}(id)
}
I have wrote a small function that might be helpful for you.
Please take a look at limited parallel operations