The value in context can not transmitted in different package? - go

Today I try to program with context,code as follow:
package main
func main(){
ctx := context.Background()
ctx = context.WithValue(ctx,"appid","test111")
b.dosomething()
}
package b
func dosomething(ctx context.Context){
fmt.Println(ctx.Value("appid").(string))
}
Then my program has crashed.I think it's due to that these ctx is in different package

I suggest you to use context only in a lifetime of a single task and pass the same context through functions.
Also you should understand where to use context and where just to pass arguments to functions.
Another suggestion is to use custom types for setting and getting values from context.
According to all above, you program should look like this:
package main
import (
"context"
"fmt"
)
type KeyMsg string
func main() {
ctx := context.WithValue(context.Background(), KeyMsg("msg"), "hello")
DoSomething(ctx)
}
// DoSomething accepts context value, retrieves message by KeyMsg and prints it.
func DoSomething(ctx context.Context) {
msg, ok := ctx.Value(KeyMsg("msg")).(string)
if !ok {
return
}
fmt.Println("got msg:", msg)
}
You can move function DoSomething into another package and just call it as packagename.DoSomething it will change nothing.

Related

Import inside of a function

As the title asks, is there any way to import a go file from inside of a function?
I was thinking of using a loop for my discordgo(https://pkg.go.dev/github.com/bwmarrin/discordgo) program.
ex :
package main
import (
...
)
func main() {
client, _ := disocrdgo.New("Bot " + mytoken)
...
events, _ := ioutil.ReadDir("./events")
for event, _ := range events {
x, _ := import "MyApp/src/events/" + event // <---
client.AddHandler(x.executor) // using type struct where executor is the function i want to use from the imported file
}
...
}
I feel obligated to precise it so :
Thanks for your SERIOUS answers.
Imports are a compiler concept, so you cannot import packages at runtime (the source code doesn't even exist on the machine running your program, usually).
You can use the registry pattern to get close to what you're looking for.
In the events package, create a function that stores handlers. Call that function in the init function for each event handler package.
In the events package, create another function that adds the stored handlers to a client.
In the main package, import all event handler packages you need and call that second function.
This is more or less how the sql and image packages in the standard library work. See sql.Register and image.RegisterFormat.
// events/registry.go
package events
var handlers = map[string]interface{}{}
func Register(name string, h interface{}) {
handlers[name] = h
}
func ConfigureClient(client *discordgo.Session) {
for _, h := range handlers {
client.AddHandler(h)
}
}
// events/foo/foo.go
package foo
import "MyApp/src/events"
func init() {
events.Register("foo", executor{})
}
type executor struct{}
// events/bar/bar.go
package bar
import "MyApp/src/events"
func init() {
events.Register("bar", executor{})
}
type executor struct{}
// main.go
package main
import (
_ "MyApp/src/events/foo"
_ "MyApp/src/events/bar"
// ...
)
func main() {
client, _ := discordgo.New("Bot " + mytoken)
events.ConfigureClient(client)
}

Using application context as queries' parent context

I am currently passing application/main context from main.go to repository.go so that I can use it as "parent" context to go with query context. Is this valid/idiomatic usage or not? Also, shall I ditch the idea and just use context.Background() as "parent" context to go with query context instead?
main.go
package main
import (
"context"
"internal/user"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
repo := user.NewRepository(ctx, db)
// HTTP server is running.
}
internal/user/repository.go
package user
import (
"context"
"database/sql"
"time"
)
type Repository struct {
*sql.DB
}
var appCTX context.Context
func NewRepository(ctx context.Context, db *sql.DB) Repository {
appCTX = ctx
return Repository{db}
}
func (r Repository) Insert(args ...interface{}) error {
ctx, cancel := context.WithTimeout(appCTX, 5 * time.Millisecond)
defer cancel()
// Run query etc.
res, err := r.ExecContext(ctx, `INSERT INTO .....`, args...)
}
The idiomatic use of context is to pass it as the first function argument, and not store in structs. This is from the context doc:
Do not store Contexts inside a struct type; instead, pass a Context explicitly to each function that needs it. The Context should be the first parameter, typically named ctx
So, even if you pass the main context down to your implementation, you should do that by passing the context to each operation.
Each self-contained operation (such as an HTTP request) should create a new context. If your main performs one such self-contained operation, you can pass the context down like this. However, if this is a server application, you should create a separate context for each request handler.

Golang - Discard as WriteCloser

I needed to create an equivalent of ioutil.Discard that can satisfy a "WriteCloser" interface. With a bit of Googling I came up with
package main
import (
"io"
"io/ioutil"
"strings"
"fmt"
)
type discardCloser struct {
io.Writer
}
func (discardCloser) Close() error {
return nil
}
func main() {
src := strings.NewReader("Hello world")
dst := discardCloser{Writer: ioutil.Discard}
count, err := io.Copy(dst, src)
fmt.Println(count, err)
err = dst.Close()
fmt.Println(err)
}
Go playground link here
Is there a more idiomatic way of doing this?
Background: some standard library methods return a WriteCloser, such as net/smtp.Data. When implementing automated tests, it's nice to be able to exercise functions like this, while sending their output to Discard.
I took bereal's tip and looked at NopCloser. The approach works nicely, and is useful in test functions built around the libraries that require a WriteCloser.
I renamed the type myWriteCloser as it can be used to promote "real" writers such as as &bytes.Buffer, as well as the special system discard writer.
type myWriteCloser struct {
io.Writer
}
func (myWriteCloser) Close() error {
return nil
}

How to call only specific functions that have an annotation or tag

Is there a way to add a tag or annotate some methods so that whenever an event arises all the methods with this tag or annotation get invoked?
How to achieve this in Go?
As others have said, no there are no annotations or tags on functions in Go.
However, it is more than possible to use a map (or slice) and a register function to keep records of other funcs (or interfaces). You just need to manually call that register func (usually in init() of that package).
For example,
package events
var eventFuncs = make(map[string]func())
func Register(event string, f func()) {
eventFuncs[event] = append(eventFuncs[event],f)
}
func Get(event string) []func() {
return eventFuncs[event]
}
And using the package to register:
package xy
import (
"fmt"
"some.url/events"
)
func Call() {
fmt.Println("Ha!")
}
func init() {
events.Register("x",Call)
}
And then call the funcs elsewhere:
package main
import (
"some.url/events"
)
func main() {
for _,f := range pkg.GetEvent("x") { f() }
}

Passing in a type variable into function

I'm trying to achieve a type assertion by passing in a type into a function. In other words, I'm trying to achieve something like this:
// Note that this is pseudocode, because Type isn't the valid thing to use here
func myfunction(mystring string, mytype Type) {
...
someInterface := translate(mystring)
object, ok := someInterface.(mytype)
... // Do other stuff
}
func main() {
// What I want the function to be like
myfunction("hello world", map[string]string)
}
What's the proper function declaration I need to use in myfunction, to successfully perform the type assertion in myfunction?
#hlin117,
Hey, if I understood your question correctly and you need to compare the types, here's what you can do:
package main
import (
"fmt"
"reflect"
)
func myfunction(v interface{}, mytype interface{}) bool {
return reflect.TypeOf(v) == reflect.TypeOf(mytype)
}
func main() {
assertNoMatch := myfunction("hello world", map[string]string{})
fmt.Printf("%+v\n", assertNoMatch)
assertMatch := myfunction("hello world", "stringSample")
fmt.Printf("%+v\n", assertMatch)
}
The approach is to use a sample of the type you'd like to match.

Resources