how to create a function parameter that won't be copied and cannot be null - go

In golang, Is it possible to create a function that takes a struct with the following constraints:
the struct must not be copied (its relatively big)
the caller must not be able to pass nil
EDIT:
I tried using pointers but that can be set to null. I can't find any good articles on how to use references and it doesn't seem like I can pass by reference.

You can create tiny struct wrapper which holds private pointer to big struct and defines Get method to allow acquisition of this big struct. Inside Get you check if pointer is nil then it panics.
Something like:
type StructHolder struct {
target *BigStruct
}
func (s StructHolder) Get() *BigStruct {
if s.target == nil {
panic("target is nil")
}
return s.target
}
Why would you do this? I'd think its better to pass a pointer and check its value.

Related

Understanding GO variable assignment

I'm new to GO and trying to build functions with the aws-sdk I have something like this
input := &rds.CreateDBClusterSnapshotInput{
// removed for brevity
}
result, err := svc.CreateDBClusterSnapshot(input)
if err != nil {
// removed for brevity
}
input = &rds.ModifyDBClusterSnapshotAttributeInput{
// removed for brevity
}
When I try to build, I get this error
cannot use &rds.ModifyDBClusterSnapshotAttributeInput literal (type *rds.ModifyDBClusterSnapshotAttributeInput) as type *rds.CreateDBClusterSnapshotInput in assignment
What's wrong with my assignment?
As #Sergio Tulentsev pointed out, you are assigning ModifyDBClusterSnapshotAttributeInput type to the variable input, that is a CreateDBClusterSnapshotInput type.
There would be a few solutions to handle this problem, but the easiest way would be to make a method for each type struct that returns a compatible type for input like this;
func (createInput CreateDBClusterSnapshotInput) ReturnInput() {
return createInput.input // assuming that there are a input type your create
}
If you don't want to make a method with the same functionality for each struct, you can create a base type, make your two structs extend the type, and build a method for the base type.

How to pass type to function argument in Go

ERROR: type CustomStruct is not an expression.
type CustomStruct struct {
}
func getTypeName(t interface{}) string {
rt := reflect.TypeOf(t).Elem()
return rt.Name()
}
getTypeName(CustomStruct)
How can I pass struct type to function without type instance?
This will work
getTypeName((*CustomStruct)(nil))
But I wonder if there is more simple version..
You can't. You can only pass a value, and CustomStruct is not a value but a type. Using a type identifier is a compile-time error.
Usually when a "type" is to be passed, you pass a reflect.Type value which describes the type. This is what you "create" inside your getTypeName(), but then the getTypeName() will have little left to do:
func getTypeName(t reflect.Type) string {
return t.Name()
}
// Calling it:
getTypeName(reflect.TypeOf(CustomStruct{}))
(Also don't forget that this returns an empty string for anonymous types such as []int.)
Another way is to pass a "typed" nil pointer value as you did, but again, you can just as well use a typed nil value to create the reflect.Type too, without creating a value of the type in question, like this:
t := reflect.TypeOf((*CustomStruct)(nil)).Elem()
fmt.Println(t.Name()) // Prints CustomStruct
Lets resurrect this!
The generics proposal for Go got approved, and that's coming, eventually. When this question was first asked, this probably made more sense as a question, but for anyone looking to implement a generics pattern now, I think I've got an alright API for it.
For now, you can't interact with abstract types, but you can interact with methods on the abstract type, and reflect allows you to examine function signatures. For a method, the 0th is the receiver.
type Example struct {int}
type Generic struct{reflect.Type}
func (p Example) Type() {}
func Reflect(generic interface{}) Generic {
real := reflect.TypeOf(generic)
if real.Kind() != reflect.Func || real.NumIn() < 1 {
panic("reflect.Type.In(n) panics if not a func and if n out of bounds")
}
return Generic{real.In(0)}
}
func (g Generic) Make() interface{} {
return reflect.Zero(g.Type).Interface()
}
func main() {
tOfp := Reflect(Example.Type)
fmt.Printf("Name of the type: %v\n", tOfp.Name())
fmt.Printf("Real (initial)value: %v\n", tOfp.Make())
}
Some quick notes:
The structure of "Example" doesn't matter, rather only that it has a method with a non-pointer receiver.
The definition of a type called "Generic" as a struct is to accomplish what I believed OP's actual intent to be.
The above definition of "Generic" is a struct instead of an interface so that it can have its own method set. Defining "Generic" as an interface, and using a methodset specific to each operand-type used with it would make tons of sense.
If you weren't aware, actual generics are coming in Go 1.18. My example above has no linter or compile protection, and will panic at runtime if used incorrectly. It does work, and will let you reason over abstract types while you wait for a native implementation.
Happy Coding!
From Go version 1.18 a new feature Generics has been introduced. In most of the case instead of passing types to function, we can use generics. Then we will also get compile time error instead of runtime error and it's more efficient than reflect also.
Example Code
func HttpGet[T](url, body) T {
var resp T
return T
}
resp := HttpGet[ResponseType]("dummy.example", nil)

Cannot use as type in assignment in go

when I compile my code, I get the following error message, not sure why it happens. Can someone help me point why? Thank you in advance.
cannot use px.InitializePaxosInstance(val) (type PaxosInstance) as
type *PaxosInstance in assignment
type Paxos struct {
instance map[int]*PaxosInstance
}
type PaxosInstance struct {
value interface{}
decided bool
}
func (px *Paxos) InitializePaxosInstance(val interface{}) PaxosInstance {
return PaxosInstance {decided:false, value: val}
}
func (px *Paxos) PartAProcess(seq int, val interface{}) error {
px.instance[seq] = px.InitializePaxosInstance(val)
return nil
}
Your map is expecting a pointer to a PaxosInstance (*PaxosInstance), but you are passing a struct value to it. Change your Initialize function to return a pointer.
func (px *Paxos) InitializePaxosInstance(val interface{}) *PaxosInstance {
return &PaxosInstance {decided:false, value: val}
}
Now it returns a pointer. You can take the pointer of a variable using & and, should you need the struct value itself, dereference it again with *.
After a line like
x := &PaxosInstance{}
or
p := PaxosInstance{}
x := &p
the value type of x is *PaxosInstance. And if you ever need to, you can dereference it back into a PaxosInstance struct value with
p = *x
You usually do not want to pass structs around as actual values, because Go is pass-by-value, which means it will copy the whole thing. Using struct values with maps and slices often results in logic errors because a copy is made should you iterate them or otherwise reference them except via index. It depends on your use-case, but your identifier Instance would infer that you would want to avoid duplications and such logic errors.
As for reading the compiler errors, you can see what it was telling you. The type PaxosInstance and type *PaxosInstance are not the same.
The instance field within the Paxos struct is a map of integer keys to pointers to PaxosInstance structs.
When you call:
px.instance[seq] = px.InitializePaxosInstance(val)
You're attempting to assign a concrete (not pointer) PaxosInstance struct into an element of px.instance, which are pointers.
You can alleviate this by returning a pointer to a PaxosInstance in InitializePaxosInstance, like so:
func (px *Paxos) InitializePaxosInstance(val interface{}) *PaxosInstance {
return &PaxosInstance{decided: false, value: val}
}
or you could modify the instance field within the Paxos struct to not be a map of pointers:
type Paxos struct {
instance map[int]PaxosInstance
}
Which option you choose is up to your use case.
For anyone else pulling their hair out: check your imports.
Not sure when it started happening, but my Visual Studio Code + gopls setup will occasionally insert an import line that references my vendored dependencies path instead of the original import path. I usually won't catch this until I start polishing code for release, or an error like this one pops up.
In my case this caused two otherwise identical types to not compare equally. Once I fixed my imports this resolved the error.

Golang calling methods on interface pointer

I am using Gorp for database access, Gorp has a standard DbMap type, as well as a Transaction type for when you want to be able to roll back. Both types implement the SqlExecutor interface.
I am therefore programming against the SqlExecutor interface, so I can use transactions if I want without needing to change my code.
I then initialise a Gorp DbMap or Transaction and pass it in as a field property. The problem is that I need a pointer to the Gorp "object", else how will I be able to roll back if I use a Transaction if the Gorp "object" is passed by value instead of reference.
I then get a message such as
r.Gorp.Get undefined (type *gorp.SqlExecutor has no field or method Get)
when I try use my code. How do I call the methods?
A code sample is below.
package repositories
import (
"github.com/coopernurse/gorp"
)
type Repository struct {
Gorp *gorp.SqlExecutor // <<<< Need pointer so I can rollback
}
func (r *Repository) GetById(i interface{}, key interface{}) interface{} {
obj, err := r.Gorp.Get(i, key)
if err != nil {
panic(err)
}
return obj
}
Maybe you are overthinking the problem, or you may still be under a "call by reference" influence from another language:
gorp.SqlExecutor is an interface and you will never use a pointer to an interface value. Never ever. (Technically this is untrue, but if you actually need a pointer to an interface, you will have mastered Go enough to understand why "never ever" is a really good advice.)
Never think about "call by reference". There is no such thing in Go. Passing a pointer to a function is not "call by reference". Leave that behind.
I assume you did not try to use transactions and do rollbacks on the non-pointer-to-interface code?
Background: In Go, you pass around a pointer to something because of two reasons:
1) You want to, because your struct is really large and you want to avoid copying, or
2) you need to, because the callee wants to modify the original (this is typical for methods with a pointer receiver).
Now an interface value is really tiny (just two words) so reason 1, to pass a pointer to an interface value, does not apply. Reason 2 does not apply in most cases as passing a pointer to an interface value will allow you to change the interface value itself, but most often you would like to modify the value stored inside the interface value. This value stored inside the interface value often is a pointer value which allows to change the value of a struct by calling methods on an interface value which wraps a pointer to this struct. This sounds complicated but isn't: The novice Go programmer just doesn't use pointers to interfaces (as this won't do any good) and the experienced Go programmer doesn't use pointers to interfaces (as it won't do much good) unless he needs to modify an interface value, typically during reflection.
gorp.SqlExecutor is an interface and you will never use a pointer
to an interface value. Never ever.
If this is your goal, you're going about it incorrectly. An
interface is a wrapper, and a contract that guarantees behavior. If
your interface requires that the methods mutate the implementing
type, then the implementing type should be a pointer. A pointer to
the interface itself will not suddenly make the implementing type
mutable.
Your struct should be
type Repository struct {
Gorp gorp.SqlExecutor
}
func (r *Repository) GetById(i interface{}, key interface{}) interface{} {
obj, err := r.Gorp.(gorp.SqlExecutor).Get(i, key)
if err != nil {
panic(err)
}
return obj
}

Instantiating a struct via name using a string in go

I am trying to create a function that takes a []byte and an interface{} (standing for the struct) and returns an interface{} as the struct type passed into the func.
Something like this:
package main
import (
"encoding/json"
)
func UnmarshalFromJSONArray(sms []byte,tt string) (interface{}) {
var ts = new(tt)
err := json.Unmarshal(sms,&ts)
if(err != nil) {
fmt.Println(err)
}
return sms
}
So that method would run something like this:
// let's say a struct has the following definition:
type MyStructType struct {
Id int
Name string
Desc string
}
// we can some how get its fully qualified class name (this may require reflection?) or pass it into the UnMarshal method direction some how.
mst := "package.MyStructType",
// and then assume a byte array ba that has JSON format for
ba := []byte(`{"Id":"3","Name":"Jack","Desc":"the man"}`)
stct := UnmarshalFromJSONArray(ba,mst)
MyStructureType out := stct
// leaving "stct" being the unmarshalled byte array which can be used like any other struct of type "MyStructureType"
The key being that I never need to know what the fields of MyStructureType are before unmarshalling. All I need are the name of the struct and some way to instance one and then populate it with JSON byte array data that matches its fields. Hopefully that is possible (it is trivial in java using reflection). So I want to basically unmarshal an anonymous struct type by it's name without needing to know what fields it has.
Any suggestions?
The short answer is that this is impossible. There is no string to type translator in Go. You can make a map of strings to reflect.Type's, but you would need to know the possible options ahead of time or you need to provide the caller with a way to register types (perhaps in init).
Assuming you have found a way to resolve the string to its reflect.Type, you can simply call reflect.New(typ).Interface() to get the pointer you need to pass to json.Unmarshal().
The best answer is to avoid trying this all together. Writing idiomatic Java in Go isn't really possible. If I knew more about your problem, I could give you a more idiomatic Go solution.

Resources