Why use non-struct types in Golang? [duplicate] - go

I am actually learning golang (from .NET) and there is one thing I don't understand about this language.
Sometimes I find this kind of declaration:
https://github.com/golang/crypto/blob/master/ed25519/ed25519.go
// PublicKey is the type of Ed25519 public keys.
type PublicKey []byte
What does it mean exactly?
Is it a creating a struct which inherit from []byte?
Is it just an alias?
I thought golang forbid inheritance.

It's a type declaration, more specifically a type definition. It creates a new type, having []byte as its underlying type:
A type definition creates a new, distinct type with the same underlying type and operations as the given type, and binds an identifier to it.
New types are created because they can simplify using them multiple times, their identifier (their name) may be expressive in other contexts, and–most importantly–so that you can define (attach) methods to it (you can't attach methods to built-in types, nor to anonymous types or types defined in other packages).
This last part (attaching methods) is important, because even though instead of attaching methods you could just as easily create and use functions that accept the "original" type as parameter, only types with methods can implement interfaces that list ("prescribe") those methods, and as mentioned earlier, you can't attach methods to certain types unless you create a new type derived from them.
As an example, the type []int will never implement the sort.Interface required to be sortable (by the sort package), so a new type sort.IntSlice is created (which is type IntSlice []int) to which the required methods are attached, so you can pass a value of type sort.IntSlice to the sort.Sort() function but not a value of type []int. Since sort.IntSlice has []int as its underlying type, if you have a value of []int, you can simply convert it to sort.IntSlice if you want to sort it, like in this example (try it on the Go Playground):
is := []int{1,3,2}
sort.Sort(sort.IntSlice(is))
fmt.Println(is) // Prints: [1 2 3]
When you create a new type, there is no "inheritance" involved. The new type will have 0 methods. If you want "inheritance-like" functionality, you should check out embedding (in relation with struct types), in which case the embedder type will also "have" the methods of the embedded type.

Related

Can I pass an *io.ReadCloser to a parameter with the type of *io.Reader

I am trying to write a function with a parameter of type *io.Reader
func ReadSomething(r *io.Reader) {
<do something with a reader>
}
but then when I try to pass in a pointer to a reader closer to it
func GetWebPage() {
resp, _ := http.Get(<url>)
ReadSomething(&(resp.Body))
}
I get the following error: cannot use &(resp.Body) (value of type *io.ReadCloser) as *io.Reader value.
This makes no sense to me, a dereferenced pointer to a read closer type should implement the methods of Reader, so why am I not able then to pass it in as an argument to a function that expects a pointer to an io.Reader?
io.ReadCloser is an interface type that is a superset of io.Reader interface type. So whenever an io.Reader is required, you may pass a value of io.ReadCloser.
*io.Reader is not an interface type though, it's a pointer (a pointer to interface), so you can't pass *io.ReadCloser.
Please note that these function signatures are a terrible design. You rarely need a pointer to interface (you'll know when you do, see this for an example). Just use interface types, interfaces may wrap pointers if needed.
It's a common misconception in Go that interfaces are more magical and gluey than they really are. As a starting point, all conversions between distinct types must be explicit, and interfaces are distinct types.
Interfaces only carve out a narrow exception to this rule:
Assignability Rules in the Go Specification (which extends to parameter passing).
A value x is assignable to a variable of type T ("x is assignable to T") if [...]
T is an interface type and x implements T.
Or in other words, simple and plain:
x is assignable to T if x implements T.
That's really the whole entire exception, and it makes no expansive effort to generally mesh interfaces, implementations, and structures thereof. As soon as you indirect your interface value through a pointer, you have stepped out of the enumerated territory.

Using msgp with interfaces and maps in Go

I have a map that uses an interface as the key. The map is defined like this MyMap map[Signature]Packets. The interface is Signature, and there will be two structs A and B that implement this interface. I am also using msgp to serialize these two structs.
My issue is that msgp automatically generates methods that use a pointer as the type of the function receiver, which I think will make the key Signature receive pointers. If that was the case, then the key would be different every single time since pointers are different, even though the underlying values are the same. So, every time, I would be creating a new entry instead of finding the existing one and modifying it.
I wonder:
Is there a way to force msgp to generate methods purely with function receivers of the concrete type? Currently, I can only modify the function receivers of auto-generated methods like MarshalMsg and UnmarshalMsg to the concrete type (A or B instead of *A or *B). By doing that, the key of the map is either of type A or of type B, and the map MyMap works fine. However, I know I should not modify the auto-generated code. So, I wonder whether there is an acceptable way to do that.
If there is no way to do 1., is there any workaround to solve this problem? I really need some polymorphic feature of the map's key with the use of msgp.
UPDATE 1 (Apr. 12):
Thanks for sharing your thoughts and offering solutions. Here are some details about my question.
The background is that the map is used for collecting different network events. The two structs implementing the interface Signature are EventSignatureIPv4 and EventSignatureIPv6
type EventSignatureIPv4 struct {
SourceIPv4 [4]byte
Port uint16
Traffic TrafficType
}
type EventSignatureIPv6 struct {
SourceIPv6 [16]byte
Port uint16
Traffic TrafficType
}
and Signature is holding common methods shared between IPv4 and IPv6 data. So, essentially, I want to collect and group corresponding IPv4/v6 events at the runtime. The key of the map is to identify the same source, and the value of the map is to collect events with different destinations.
The msgp library I am using is this one https://pkg.go.dev/github.com/tinylib/msgp#v1.1.5/msgp
Correct me if I am wrong. For compositions in Go, if one of the methods in the method set has a function receiver of the pointer type, then the instance would only be of the pointer type? So here, as I have
func (z *EventSignatureIPv6) MarshalMsg(b []byte) (o []byte, err error) {
/* Auto-generated code */
}
whenever I use Signature to receive the struct EventSignatureIPv6, the struct would only be of type *EventSignatureIPv6?
You are right, "two pointer values are equal if they point to the same variable.", so if you are looking to compare interfaces that may hold pointers to different types, e.g. *A and *B, you are already in trouble.
With that said, I don't think it's an amazing idea to use interface types as map keys in the first place, because you have to deal with some caveats, the first is that:
The comparison operators == and != must be fully defined for operands of the key type
And now you need to be careful about the types that implement the interface. In theory, nobody stops a client from implementing your interface on a defined type with underlying unhashable type, e.g. type UncomparableSignature []int
So you would probably have to add an unexported method on your interface, so that client code outside that package can't implement it. But still, nothing stops code within the same package from implementing it, so this is, at best, maintenance overhead.
Then if the interface holds pointers to zero-values, it's even dependant on the implementation of the specs:
Pointers to distinct zero-size variables may or may not be equal.
Furthermore, you open yourself up to pesky bugs, like variables of type Signature that holds a nil will overwrite each other's values:
var foo Signature
var bar Signature
myMap[foo] = &Packet{/*pretending to have value 1*/}
myMap[bar] = &Packet{/*pretending to have value 2*/}
fmt.Println(myMap[foo]) // 2
A possible solution is, you could replace the map key with a unique id, and you enforce implementors to provide it by declaring the appropriate method on the interface Signature (this still assumes that the implementors can be coordinated to provide unique ids across all of them):
type Signature interface {
UniqueIdent() uint64 // or string, if you prefer
// ...other methods
}
and then
packet := myMap[someSignature.UniqueIdent()]

Inheritance syntax. What is the difference?

In Go what is the difference below?
I tried to search, but I couldn't think of a right term to describe them. How are they officially called?
#1 - Parent inside struct:
type MyTime struct {
time.Time
}
#2 - Parent right after my type without struct definition:
type MyTime time.Time
For #2, I can initialize my type with another time.Time like mt := MyTime(t).
How to initialize #1 type with another time.Time variable?
Go is not an object oriented language, there is no type hierarchy and inheritance. For details, see What is the idiomatic way in Go to create a complex hierarchy of structs? and Go embedded struct call child method instead parent method.
Your first example creates a new type MyTime embedding time.Time. By embedding a type, all fields and methods of the embedded type gets promoted and are available as if they were fields or methods of the embedder type, very handy when you want to implement an interface for example (because the promoted methods will "exist" without having to declare them).
You can instantiate it like (for details, see Golang embedded struct type):
t := MyTime{Time: time.Now()}
Your second example creates a new type, all methods will be stripped from it. This comes handy when you do not want the existing methods to be part of the new type. A shining example of this is to define the String() method or override marshaling behavior when you want to call the original behavior in your implementation, for details see Call json.Unmarshal inside UnmarshalJSON function without causing stack overflow. You can create a value of it using a simple type conversion:
t := MyTime(time.Now())

How to write idiomatic constructor

I'm confused about the constructors in Go. Most constructors I've seen return a struct, but 'Effective Go' suggests that an interface can be returned in some cases, according to the rule of 'Generality'.
I trust 'Effective Go' to provide good ideas, but this doesn't seem to follow the principle of 'accept interfaces, return structs'. I guess that many types implement an interface and nothing more than that, so in that case it would be common to see constructors which return interfaces.
Another related statement is that interfaces should be defined by the consumer, but 'Generality' means that the interface is defined by the producer.
Can someone clarify?
As it has already been mentioned, returning an interface should be considered something exceptional.
Returning errors of type error which is an interface is one of those exception.
Returning an interface that represents an unexported type is the other exception. But why would you have an exported interface that describes an unexported struct instead of just having an exported struct?
The reason is simple, that allows you a higher degree of control on how that struct is constructed.
Compare this two pieces of code:
type MyType struct {
MyField string
}
func NewMyType(value string) MyType {
return MyType{value}
}
func (t MyType) MyMethod() string {
return t.MyField
}
type MyType interface {
MyMethod() string
}
type myType struct {
MyField string
}
func NewMyType(value string) MyType {
return myType{value}
}
func (t myType) MyMethod() string {
return t.MyField
}
In the first case I would be able to do: myVar := MyType{} while in the second case I won't be able to do so, I am forced to use the provided constructor. The first case also allows to modify the field value after creation which is not allowed in the second case. Making the field unexported will solve the second part but not the first.
This example is obviously trivial, but being able to construct invalid structs may have a horrible impact. By having specific constructors you can ensure that the object is in a valid starting state and you will only need to make sure it always stays in a valid state. If you can't ensure that, you may need to check that it is in a valid state at the start of every method.
For example consider a DB request. It needs a DB connection. If the user is able to create a DB request without a DB connection you will have to check that it is valid in every method. If you enforce him to use a constructor you can check at creation time and done.
It depends a bit on your preference and how you view things. Coming from OOP background my take is: there is no point in the constructor if you cannot enforce it. Adding the constructor means - you must supply these values when instantiating this item. If your struct is public, it will be misused and instantiated bypassing the constructor. So it makes sense that the constructor returns the public interface and the struct is private (lowercase). If the struct is public, there is no point in the constructor, because you cannot enforce it. Writing code is a dialogue between writer and reader, making a struct public and having a constructor would tell the reader - here you have the constructor, but you also have a public struct and that would mean that constructor usage is arbitrary. If that is the case, go with that setup
In most cases constructor functions return concrete types (or pointer to a type).
The situations in which returning interfaces might be a good idea is when calling factory functions or builder functions in which underlying concrete type satisfies that interface.
Consider error interface for example, when you call http.NewRequest underlying concentrate error type can be of net.Error, net.DNSError etc. Now try to think how are you going to create an api like this without an error interface if function returns concrete type? Only solution to it I can think of is to create a massive error type for net package and add fields for extra information, but its most probably much harder to maintain, test that kind of error type and not to mention memory bloat.
Whether you choose to return concrete type or an interface is a design choice, some guidelines exists to give solution to common scenarios.

Go: Embedding a primitive type?

Suppose we have this piece of code:
type User struct {
int32
Name string
}
Can this type of embedding be useful?
Does int32 have any methods which other can call on instances of User?
How can I access the value of the int32 that User is embedding?
The type int32 is a predeclared type, it has no methods. To verify:
fmt.Println(reflect.TypeOf(int32(0)).NumMethod()) // Prints 0
You can refer to all embedded fields by using the unqualified type name as the field name (Spec: Struct types), predeclared types are no exception. See this example:
u := User{3, "Bob"}
fmt.Printf("%#v\n", u)
u.int32 = 4
fmt.Println(u.int32)
Output (try it on the Go Playground):
main.User{int32:3, Name:"Bob"}
4
Primary gain of using embedding is:
methods of the embedded type get promoted, so it's easier to implement interfaces (you don't need to provide methods that get promoted)
you can "override" methods of the embedded type (on the embedder type): provide your own implementation which will be called when a value of your embedder type is used
and fields of the embedded type get promoted, so code is shorter to refer to promoted fields (field name is left out).
By embedding predeclared types such as int32, you don't get any advantage over using just a regular field (named, not embedded field), as the int32 type doesn't have any methods or fields.
Going forward, besides not having any advantage, you even have a disadvantage. Since predeclared type names start with lowercased letters, embedding them implicitly makes them unexported, so you can only refer to them in the declaring package of the embedder type. If you make them regular, named fields, you may choose to use an uppercased name to make it exported, or a lowercased name to make it unexported.

Resources