mismatched types *string and string - api

I am attempting to run a conditional to basically see if the object is empty but I keep getting (similar variations) of this error:
invalid operation: release.Name == "" (mismatched types *string and string)
Here is the code that is dying:
import (
"github.com/google/go-github/github"
)
func TestLatestTag(user, project string) {
var client *github.Client
client = github.NewClient(nil)
releases, _, err := client.Repositories.ListTags(user, project, nil)
var release github.RepositoryTag
if err != nil {
fmt.Println("Error")
} else {
if release.Name == "" {
fmt.Println("None")
} else {
fmt.Println(releases[0])
}
}
}
If I change the if statement to *release.Name == "" as the error suggests I get a different error, which I don't really understand:
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xb code=0x1 addr=0x0 pc=0x26fd]
goroutine 1 [running]:
I'm sure there is any easy way to do this but I am not very familiar with handling objects/structs

From the error message it looks like you are trying to compare a string pointer (*string) to an actual string.
release.Name is a *string (a pointer to a string value)
"" is a string (is a string value)
They are two different types. So you can't compare them.
What you probably want to do instead is release.Name == nil
When a pointer that references to nothing (equals to nil) is tried to be dereferenced you get that second error. So in your case *release.Name panics because infact release.Name is nil

var release github.RepositoryTag
You never assign any value to that var. That's why *release.Name gives you a "runtime error": release.Name is a nil pointer

As per your code you have declared var release github.RepositoryTag, but you have not initialized it.
In structure RepositoryTag, Name is declared as *string which is a pointer and in case of release.Name == "", string comparison is attempted which is incorrect hence "mismatched types *string and string" error.
In case of *release.Name == "", since release is not yet initialized, it is complaining "invalid memory address or nil pointer dereference"
You need to do two things, 1st initialize, release and second, check release.Name = nil.

Related

Gorm - how to access nullable fields when using pointers?

I'm new to Go and still learning about optional struct fields.
I'm looking at the Gorm manual page where it gives an example of using pointers to indicate nullable fields (here)
If I strip down the example given so that it contains only a mandatory field and an optional field I'm left with something like this:
https://play.golang.com/p/lOLGWNVvq1l :
package main
import "fmt"
type User struct {
Name string
Email *string
}
func main() {
user := User{Name: "Example"}
// cannot use "example#example.com" (untyped string constant) as *string value in assignment
user.Email = "example#example.com"
// invalid operation: user.Email == "example#example.com" (mismatched types *string and untyped string)
if user.Email == "example#example.com" {
fmt.Println("foo")
}
}
How would I perform operations on a record that I've just retrieved from the database?
I need to be able to check if a nullable field is set to some value. I also can't assign a value to it.
One approach that I've thought of is to use some sort of wrapping function to try and make things safer, like at https://play.golang.com/p/4YlpPwaXMkm where I have:
func UnwrapString(x *string) string {
if x != nil {
return *x
}
return ""
}
func WrapString(x string) *string {
return &x
}
func main() {
user := User{Name: "Example"}
// can safely set an optional value that is currently null
if UnwrapString(user.Email) == "example#example.com" {
fmt.Println("hello world")
}
// can safely set a value if the existing Email is null
user.Email = WrapString("example#example.com")
// only safe because the value is set
if *user.Email == "example#example.com" {
fmt.Println("hello world")
}
}
Working with nullable fields in Gorm seems like such a basic and common thing that I don't expect to have to DIY. What's the idiomatic Gorm way to do this?
An idiomatic way to check if a field is non-nil, and if so, compare the value:
if user.Email != nil && *user.Email == "example#example.com" {
fmt.Println("foo")
}
The reason this works, even if user.Email is nil (and you get no nil-pointer dereference panic), is because Go has short circuit evaluation, meaning if the first comparison falls through in this AND statement, the second won't be evaluated, because there is no way this AND statement will ever be true if the first value is already false.
To do inline pointer assignments, the function you wrote is what I would do as well:
func StrPtr(s string) *string {
return &s
}
And you could then do:
user.Email = StrPtr("example#example.com")

err value disappearing outside of if statement

I have the code below:
if err == nil {
body, err := ioutil.ReadAll(response.Body)
if err == nil {
dataMap := &models.UserResponse{}
json.Unmarshal(body, &dataMap)
if dataMap.User == (models.UserId{}) {
err = fmt.Errorf("unauthorized")
fmt.Println(err) // when unathorized, prints unauthorized
}
}
}
fmt.Println(err) // always prints nil
The Println inside if dataMap.User ... prints "unauthorized", whereas the last Println always prints nil.
I have no idea why it happens, err is declared via var err error at the beginning of this function.
The cause is detailed in Spec: Short variable declaration:
Unlike regular variable declarations, a short variable declaration may redeclare variables provided they were originally declared earlier in the same block (or the parameter lists if the block is the function body) with the same type, and at least one of the non-blank variables is new. As a consequence, redeclaration can only appear in a multi-variable short declaration. Redeclaration does not introduce a new variable; it just assigns a new value to the original.
When using short variable declaration with multiple variables where one already exists, assignment will only happen to the existing variable if it was declared in the same block. Since in your case err variable existed before the if block, a new err variable will be created inside the if block, which has nothing to do with the "outsider" err variable (other than sharing its name). The outer err will be shadowed in the if block after the short variable declaration.
So what happens is that inside the if, you create a new err variable, and you assign a value to that, and you print that.
After the if statement, you will print the outer err variable whose value was not changed inside the if block, so it remains nil.
See this example:
var err error
fmt.Println("outside:", err) // nil
{
// A new err variable, shadows the outer:
i, err := strconv.Atoi("a")
fmt.Println("inside:", i, err) // 0 strconv.Aoti: parsing "a": invalid syntax
}
fmt.Println("outside:", err) // nil, because this was never changed
// Now this will change the "outer" err:
j, err := strconv.Atoi("a")
fmt.Println("outside:", j, err) // 0 strconv.Aoti: parsing "a": invalid syntax
Output (try it on the Go Playground):
outside: <nil>
inside: 0 strconv.Atoi: parsing "a": invalid syntax
outside: <nil>
outside: 0 strconv.Atoi: parsing "a": invalid syntax
If you want to use (assign to) the "outer" variable when also creating new variable(s), you can't use short variable declaration in a "nested" block but only simple assignment, in which case you also have to declare the other variables a priori, like in this example:
if err == nil {
var body []byte
body, err = ioutil.ReadAll(response.Body)
// ... rest...
}
See related questions:
Why it is possible to redefine err in multiple return statement in Go
Why there are two ways of declaring variables in Go, what's the difference and which to use?
You have created err inside block but you are printing same outside of block. Declare error outside of block and initialize it anywhere inside that will fetch the value when printing.
var err error
fmt.Println(err) // always prints nil

How to delete S3 object in AWS-SDK-Go?

I want to delete bucket/userID.
But there are many files under bucket/userID
I have to implement to delete bucket/userID, need using ListObjects then DeleteObjects.
The function ListObjects returns result.Contents is []*s3.Object
But DeleteObjects needs []*s3.ObjectIdentifier.
I can't convert []*s3.Object to []*s3.ObjectIdentifier.
In this code, error occurred invalid memory address or nil pointer dereference
type Object struct {
_ struct{} `type:"structure"`
ETag *string `type:"string"`
Key *string `min:"1" type:"string"`
LastModified *time.Time `type:"timestamp"
timestampFormat:"iso8601"`
Owner *Owner `type:"structure"`
Size *int64 `type:"integer"`
StorageClass *string `type:"string" enum:"ObjectStorageClass"`
}
type ObjectIdentifier struct {
_ struct{} `type:"structure"`
Key *string `min:"1" type:"string" required:"true"`
VersionId *string `type:"string"`
}
objects := getObjects() // return []*s3.Object
a := make([]*s3.ObjectIdentifier, len(objects))
for i, v := range objects {
a[i].Key = v.Key
}
a[i].Key = v.Key is error.
How to implement to delete bucket/userID?
The Go developer guide has a topic, with code, on deleting all objects in a bucket: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/s3-example-basic-bucket-operations.html#s3-examples-bucket-ops-delete-all-bucket-items
In you implementation, a := make([]*s3.ObjectIdentifier, len(objects)) only declares those variables. It does not initialize the array for every struct. As a result, it would create a nil pointer exception.
You would need to initialize all struct in your iteration:
...
for i, v := range objects {
a[i] = &s3.ObjectIdentifier{
Key: v.Key,
}
}
After you construct the []*s3.ObjectIdentifier, you could call DeleteObjects with DeleteObjectsInput parameters according to AWS Golang's doc.

crash if CFDictionaryRef does not exist

i have an issue when disconnecting ethernet-cable from computer or just turned off ethernet. in this case some entrys do not exist and my app would crash.
so i tryed to find out how to prevent and just found CFDictionaryContainsKey, but this does not prevent the error. Anybody who knows an workaround which is also working lower than osx 10.6 ?
- (NSString *)checkNetworkInterface
{
SCDynamicStoreRef ds = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("myapp"), NULL, NULL);
CFDictionaryRef dr = SCDynamicStoreCopyValue(ds, CFSTR("State:/Network/Global/IPv4"));
Boolean ck = CFDictionaryContainsKey( dr, CFSTR("PrimaryInterface"));
NSString *interfaceString;
if (ck) {
CFStringRef interface = CFDictionaryGetValue(dr, CFSTR("PrimaryInterface"));
interfaceString = [NSString stringWithString:( NSString *)interface ];
} else {
interfaceString = [NSString stringWithString:#"" ];
}
CFRelease(dr);
CFRelease(ds);
return interfaceString;
}
if "State:/Network/Global/IPv4" does not exist, app crashes :(
As the documentation for SCDynamicStoreCopyValue() states:
Return Value: The value associated with the specified key, or NULL if no value was located or if an error occurred. You must release the returned value.
CFDictionaryContainsKey() attempts to inspect the passed-in dictionary; if it's NULL, you crash with a NULL pointer dereference. You also shouldn't CFRelease() a NULL pointer.
To correct this, just add a NULL check before calling CFDictionaryContainsKey().
NSString *interfaceString;
if(dr != NULL && CFDictionaryContainsKey(dr, CFSTR("PrimaryInterface")))
{
CFStringRef interface = CFDictionaryGetValue(dr, CFSTR("PrimaryInterface"));
...
CFRelease(dr);
}
I think you just want to check whether dr == NULL and abort if so. Apologies if this is a little obvious, but you're not doing it here and it seems like it would ward off the crash.

Can an If Statement tell if an assignment was valid?

I have an object that returns a value if successful and false (or nil) if it failed.
i want to assign that value to a variable
if(var1 = [object foo])
{
//if the [object foo] returned a variable, goes here
}
else
{
//[object foo] returned FALSE (or nil), go here
}
can an If statement detected if an assignment was valid?
This is all right but will generate a warning, since this is a common typo (= instead of ==). To silence that warning add another set of parentheses like this:
if ((var = [object foo])) ...
Since this easily can lead to misunderstandings a lot of people will advise against doing this. For a simple if statement this is much clearer to do the assignment first:
var = [object for];
if (var) ...
In while loops this is more useful, but also considered harmful by many people.
Not sure I understand your question, but let me try and explain a few situations you can check
1) Property contains value
if ([object foo])
{
// If foo has a value associated to it that is not nil/false/zero
}
else
{
// If foo equals nil, false or zero
}
2) Assignment to a variable was successful
if ((bar = [object myMethod]))
{
// If myMethod returns any non-nil value
}
else
{
// If myMethod returns nil
}
3) Previous assignment of a variable was successful
bar = [object myMethod];
if (bar)
{
// If bar has a value associated to it that is not nil/false/zero
}
else
{
// If bar equals nil, false or zero
}
use == instead of = in the if statement.
before the if statement, you may have var1 = [object foo]
see comparison operators
If you mean by valid that the variable contains an expected result, you can just perform another if on the variable against the expected result, or null to check it.