I am practicing writing idiomatic Go code and discovered that interfaces should be declared in packages which are consuming them since they're implicit. However I came to this situation where by in the second package (package b) I want a function to call the receiver function of a struct in package a without coupling it tightly.
So naturally, I declare an interface in package b with the signature of the function I want to call from package a. The problem is that this function accepts an argument of a certain type which is an interface declared in package a. As I don't want package b to import package a, I defined an interface in package b with the exact same signature as the one which exists in package a. The playground link below shows the example code.
Playground
package main
import (
"fmt"
"log"
)
func main() {
manager := &Manager{}
coach := NewRunnerCoach(manager)
fmt.Println("Done")
}
// package a
type Runner interface {
Run()
}
type Manager struct {
}
func (o *Manager) RegisterRunner(runner Runner) {
log.Print("RegisterRunner")
}
func (o *Manager) Start() {
log.Print("Start")
}
// package b
type RunnerCoach struct {
runner *FastRunner
}
func NewRunnerCoach(registerer runnerRegisterer) *RunnerCoach {
runnerCoach := &RunnerCoach{&FastRunner{}}
registerer.RegisterRunner(runnerCoach.runner)
return runnerCoach
}
type FastRunner struct {
}
func (r *FastRunner) Run() {
log.Print("FastRunner Run")
}
// define ther registerer interface coach is accepting
type runnerRegisterer interface {
RegisterRunner(runner RunnerB)
}
// declaring a new interface with the same signature because we dont want to import package a
// and import Runner interface
type RunnerB interface {
Run()
}
This code does not compile. So the question here is that, am I using interface wrongly or should concrete types be defined in a separate package or lastly, is there a better code pattern for the problem I'm trying to solve?
EDIT: To clarify, package a and b does not import each other. The main() code exists in a separate package which connects these two.
IIUC, your question is not about packages but boils down to whether a function (or method)
can be typecast to another function which takes arguments with equivalent, but
not the same interface types.
Something like this: (Go Playground)
package main
type I1 interface{}
func f1(x I1) {}
func main() {
f := (func(interface{}))(f1)
f(nil)
}
Compilation error: ./g.go:8:26: cannot convert f1 (type func(I1)) to type func(interface {})
The answer appears to be no, because Go doesn't consider func (I1) to be
equivalent to func (interface{}). The Go spec says this
A function type denotes the set of all functions with the same parameter and result types.
The types func (I1) and func (interface{}) do not take the same parameters, even though
I1 is defined as interface{}. Your code fails to compile for a similar
reason because func (runner RunnerB) is not the same as func (runner Runner) and hence the method set of *Manager is not a superset of the
interface runnerRegisterer.
Coming to your original question:
I am practicing writing idiomatic Go code and discovered that interfaces
should be declared in packages which are consuming them since they're
implicit.
Yes, the idea is good but it doesn't apply to your implementation the way you
think it does. Since you're expecting to have different implementations of
runnerRegisterer and they must all have a method with the same signature
using the Runner interface, it makes sense to define Runner in a common
place. Also, as seen above Go wouldn't allow you to use a different interface
in the method signatures anyway.
Based on my understanding of what you're trying to achieve, here's how I think
you should re-arrange your code:
Define RunnerRegisterer (note: this is public) and Runner in one
package.
Implement your RunnerCoach in the same package and use the above
interfaces. Your RunnerCoach consumes types that implement the interfaces,
so it defines them.
Implement your runners in another package. You don't define the Runner
interface here.
Implement your Manager in another package, which uses the interface
Runner defined in RunnerCoach's package because it must take that type
as an argument if it wants to be used as a RunnerRegisterer.
The Solution
You've got a type that is used in two packages, A and B. Package A imports package B.
You have to avoid circular dependencies, so you have three choices:
Define the type in package B, so it will be available in both
packages.
Define the type in package C and have both A and B import package C.
Change your design such that the type is not used in both A and B.
These are your choices whether that type is an interface or any other type.
Choose the option that best fits your design goals.
The Rule / Idiom
I am practicing writing idiomatic Go code and discovered that
interfaces should be declared in packages which are consuming them
since they're implicit.
I get the impulse/idea - the problem is it's not very practical. Interfaces would be less useful if they could only be consumed in the package in which they are defined. The standard library is full of code that violates this axiom. Therefore I do not think the rule as presented -- applying to all interfaces in all situations, designs and contexts -- is idiomatic.
For example, look at the bytes package. func (b *Buffer) ReadFrom(r io.Reader) (n int64, err error) consumes io.Reader, an interface defined in another package. func (r *Reader) WriteTo(w io.Writer) (n int64, err error) consumes io.Writer, an interface defined in another package.
Related
Any help here is appreciated! I'm sure that I'm missing something really basic.
The problem I have is I am trying to get a value out of context in a demo web application, and I'm receiving the error:
2021/04/11 11:35:54 http: panic serving [::1]:60769: interface conversion: interface {} is nil, not []string
In my main function I'm setting the context with the following:
package main
type ctxKey struct{}
func someHttpHandleFunc() {
// .....
ctx := context.WithValue(r.Context, ctxKey{}, matches[1:])
route.handle(w, r.WithContext(ctx))
}
Then in my handler, I have the following:
package some_package
type ctxKey struct{}
func getField(r *http.Request, index int) string {
fields := r.Context().Value(ctxKey{}).([]string)
return fields[index]
}
I know that I'm missing something simple because if I try the above code and put my getField() function within the package main everything works.
For reference, this is a learning exercise, I'm trying to teach myself Go routing. I do know that there are routing packages available - but my goal is to learn. I'm trying my best to follow along with Different approaches to HTTP routing in Go. I have also read through Pitfalls of context values and how to avoid or mitigate them in Go. The latter seems to directly address the problem I'm having, but I can't seem to figure out how to solve it based on what is there.
Defined struct types defined in different packages are different.
package main
type ctxKey struct{}
is not the same type as
package some_package
type ctxKey struct{}
To make it intuitively clearer, think that if you were to reference these types from a third package — let's pretend the types were exported — you would have to import the corresponding package and use the appropriate selector:
package baz
import (
"myproject/foo"
"myproject/some_package"
)
func doBaz() {
foo := foo.CtxKey{}
bar := some_package.CtxKey{}
}
Now, the implementation of Value(key interface{}) uses the comparison operator == to determine whether the supplied key matches:
func (c *valueCtx) Value(key interface{}) interface{} {
if c.key == key {
return c.val
}
// then it checks if the key exists on the parent
return c.Context.Value(key)
}
Which implies that the types also must match. From the specs, comparison operators
In any comparison, the first operand must be assignable to the type of the second operand, or vice versa.
And clearly in your example ctxKey struct{} declared in main is not assignable to ctxKey struct{} declared in some_package, and vice-versa, because their types differ.
To solve your error, make sure the keys used when setting and getting the context value are the same type. The best way, also to ensure proper encapsulation, would probably be to set and get the context value from the same package:
package some_ctx_helper_pkg
// unexported, to ensure encapsulation
type ctxKey struct{}
func Set(ctx context.Context, value interface{}) context.Context {
return context.WithValue(ctx, ctxKey{}, value)
}
func Get(ctx context.Context) interface{} {
return ctx.Value(ctxKey{})
}
I realize questions about cyclic dependencies have been answered before, however, the answers usually simply say to merge packages. So long story short, I have an interface which many types will implement. I want to have a way of choosing which one of these types is to be used during runtime using its name. This would also be used for serialization. I read in the name of the class and then instantiate the correct one.
I have used the strategy pattern. There is a Base interface in package A.
package A
import (
"../C"
)
type Base interface {
doStuff(p C.Profile) int
}
type Operation struct {
Base Base
Data int
}
func (o *Operation) execute(p C.Profile) int {
return o.Base.doStuff(p)
}
Then, there are types that implement that interface in package B.
//file Impl1.go
package B
import (
"../C"
)
type Impl1 struct {}
func (b *Impl1 ) doStuff(p C.Profile) int {
...
}
//file Impl2.go
package B
import (
"../C"
)
type Impl2 struct {}
func (b *Impl2 ) doStuff(p C.Profile) int {
...
}
Then in package C i have struct Foo with a field of type Base. This field can point to any of the implementations from package B. We can choose the implementation during runtime. This is the struct that I eventually want to serialize.
package C
import (
"../A"
"../B"
)
type Foo struct {
bar A.Base
baz []Profile
...
}
func (f *Foo) changeBar(name string, data int) {
switch name {
case "Impl1":
f.bar = Operation{Base: B.Impl1{}, Data: data}
case "Impl2":
f.bar = Operation{Base: B.Impl2{}, Data: data}
...
}
EDIT: Also in the C package, we have the Profile, which is the reason A and B packages need to import it.
This code has a cyclic dependency C -> B -> C. An obvious solution would be to move Profile to a different package. But this is not possible because Profile (and many other similar types in the C package) and Foo are very tightly coupled and belong in the same package (maybe not so apparent in this minimal working sample). This is what other answers to these type of questions suggest, but I want to learn how to make it work, using good practices i learned from using other languages.
Another solution would be to somehow move the factory method changeBar to another package and only use it on the outside of the C package (thus avoiding the cycle), passing it results to C as parameters, but there are cases (serialization in particular) where I actually need it on the inside of the C package.
I have put a lot of time into figuring out this issue but all I end up with is either have everything in one huge package or have every single file in a separate package and have everything be exported. In other programming languages, there is the possibility to either have these cyclic dependencies or import one "class" at a time from a package. What is the way to go in go?
You can break the dependency from C to B using a type registry:
package C
import "A"
var TypeRegistry = map[string]func() A.Base {}
func (f *Foo) changeBar(name string, data int) {
if factory, ok:=TypeRegistry[name]; ok {
f.bar=Operation{Base:factory(),Data:data}
}
}
Then register your implementations in any package:
package B
import "../C"
func init() {
C.TypeRegistry["myType"]=func() A.Base { return MyType{}}
}
We have 2 packages that implement the same function signatures. In my main I need to switch out the package being used based on a condition:
import (
packageA "deployment/sdk/packageA"
packageB "deployment/sdk/packageB"
)
someCondition := true
var packageToUse ?
if someCondition {
packageToUse = packageA
} else {
packageToUse = packageB
}
packageToUse.DoSomething()
Of course, this doesn't compile. I don't know what type to use for packageToUse. This approach is probably an anti-pattern. I'm just not sure the approach I should be taking.
any suggestions?
The error I get is use of package without selector
It is very much a pattern - aside from the dynamic import. It is called „Interfaces“.
Given an interface definition like
type Stringer interface{
String() string
}
and the type Foo implementing said interface
type foo int
func(f foo)String() string{
return fmt.Sprintf(”%d”,f)
}
as well as Bar implementing said interface
type bar string
func (b bar) String()string {
return string(b)
}
one can actually use both as a parameter for a function baz
func baz(s Stringer){
fmt.Println(s.String())
}
Note that unlike other languages, you do not have to declare the interfaces a type implements - as long as a type happens to factually implement an interface, the go compiler is ok with it. Run on Playground
So, with regards to your question of package imports: Unless you have ginormous dependency trees to import, it really is not worth the hassle.
Say we talk of an application using either BBolt or etcd and for good measure some MongoDB. The resulting size to include all of them, while relatively speaking is quite astonishing, is negligible. I compiled a go program with imports of bbolt, bbolt & etcd and bbolt, etcd & mongodb. These are the results in bytes.
11734884 size_all
2455544 size_bolt
10307700 size_boltetcd
We are talking of a few megabytes in file size. So say you use either Bolt or etcd or MongoDB, do you really want to jump through all the hoops and loops required to get dynamic imports done properly? I for my part would neither want to do it nor would I want Go to provide such a feature.
"Premature optimization is the root of all evil (or at least most of it) in programming."
– Donald Knuth
This isn't quite what you asked, but this is how I would accomplish the goal if I were you:
// I assumed both package functions have the same signature, so you can use a type
// TODO: Actual function signature you want to use here
type myFn func() error
var fnToUse myFn
if someCondition {
fnToUse = packageA.Func1
} else {
fnToUse = packageB.Func2
}
fnToUse()
Following up a bit on comment about using an interface
I would step back a bit and see if this can be better solved using an
interface that you provide the concrete class for in the condition
statement.
package packageA
type Thing struct {
}
func (t Thing) DoSomething() {
}
--
package packageB
type Thing struct {
}
func (t Thing) DoSomething() {
}
--
package main
import (
"packageA"
"packageB"
)
type PackageUser interface {
DoSomething()
}
func main() {
var pu PackageUser
if (condition) {
pu := packageA.Thing{}
} else {
pu := packageB.Thing{}
}
pu.DoSomething()
}
I am not sure if these should be two separate questions or one, but it seems to me as one question of two parts - How go interfaces are supposed to be used? I have this two struggles:
The methods of the interfaces are globally scoped: If I have interface A and interface B that both implement the same method Foo, but with different arguments or return types I am unable to implement both at the same time from the same type. For example, I have GetBytes() method in one interface having return type []byte and in another ([]byte, error) How I should overcome this issue?
Another issue I have is when I try to define interface say interface A that has a method that returns interface B that is defined in the same layer. Now if I want to create an object that implements A, if I return struct that implements B go is not smart enough to deduce that this method implements the method in A and it forces me to create dependency on B. This seems to completely defeat the point of the way interfaces work in go at first place. How can I avoid this issue?
for example, if I have:
type B interface {
Bar()
}
type A interface {
Foo() B
}
for the following structs :
type b_impl struct{}
func (b b_impl) Bar() {}
type a_impl struct{}
A foo method
func (a a_impl) Foo() b_impl {}
does not satisfy the interface A and I need to make it:
func (a a_impl) Foo() B {}
which makes a dependency to the package where B is declared.
1st question:
In go you need to make different function names, when you want to do different tasks. Let's look into the standard library at the strconv package how things are solved there: https://golang.org/pkg/strconv/#pkg-index
Look to the different declaration of the append function there. There are functions for every different type.
So if you expect a FooInt funtion your interface should be also a FooInter, ...
2nd question:As a small example. You don't need to import the whole io package, when you want to use the io.Writer interface. It is totaly ok to copy the Writer declaration into your own package. If you do that correct, every io.Writer implementation will automatically implement your own Writer interface.
After reading the other comments maybe you have a different situation:
Let's say there is a package a and b with the interface a.A and b.B. If there is the situation that:
type A interface{
Foo() b.B
}
and you have to write an implementation for a.A, then you need to import package b. But that makes your binaries not bigger, because you will always need to import package a, which depends on b.
For implementing an interface, you need to have exactly the same method name and signature. Means the signature must be with the same types.
As much as it seems weird, these two don't have the same signature:
Foo() B
Foo() b_impl {}
It doesn't matter that b_impl implements B.
To overcome the need to import the package B is declared in, you can use anonymous type. This way the compiler won't require a specific type for the method signature.
type A interface {
Foo() interface{Bar()}
}
Which also can be written as embedding B:
type A interface {
Foo() interface{B}
}
Still, a_impl must have same signature, so it also needs to return an anonymous interface. You can also declare the same B interface in the package of a_impl, and embed it in an anonymous interface.
package impl
type B interface {
Bar()
}
type b_impl struct{}
func (b b_impl) Bar() {}
type a_impl struct{}
func (a a_impl) Foo() interface{B} {
return b_impl{}
}
I don't know if it's a bad practice or not, probably there are cases which redesign is required instead of doing this.
Consider the following simple go program
package main
import (
"io"
"encoding/json"
"os"
)
type MyEncoder interface {
Encode(v interface{}) error
}
type MyEncoderCreator func(io.Writer) *MyEncoder
type MyContainer struct {
Creator MyEncoderCreator
}
func main() {
container := Container{
Creator:json.NewEncoder,
}
encoder := container.Creator(os.Stdout)
encoder.Encode(map[string]string{"key":"value"})
}
This program fails to compile with the following error:
./main.go:21: cannot use json.NewEncoder (type func(io.Writer) *json.Encoder) as type MyEncoderCreator in field value
Why is this? The json.Encoder struct has a receiver that satisfies the MyEncoder interface. So should the json.NewEncoder function be allowed to assigned to MyContainer.Creator?
Yes, a function has to satisfy the exact signature of the function type. Similar things come up in other contexts: a more formal way to say it is that generally types in Go lack covariance. Here, you could wrap json.NewEncoder in a function returning MyEncoder.
I'd use the value type MyEncoder not the pointer *MyEncoder because if you store a pointer inside the interface value then generally you don't also need a pointer to the interface value just to avoid copies; here's more on pointers vs. values if it helps.