Skip to content
This repository was archived by the owner on May 24, 2023. It is now read-only.

Consider pointers to string (Int, etc) rather than NullString (Int, etc) #32

Closed
carbocation opened this issue Dec 9, 2013 · 7 comments

Comments

@carbocation
Copy link

Using sql.NullString as a property type leaves a bad taste in the mouth when designing structs, as it seems to tightly couple SQL to models that may generally be independent of SQL.

I've found that using *string (*int, etc) is more palatable. With automatic dereferencing, it hasn't been much of an issue. I'm curious re: your take on this, and re: your thoughts on including it in the section on NULL values.

@arnehormann
Copy link
Contributor

*string is stored in an interface, so you have 3 levels of indirection (interface -> pointer to string -> pointer to bytes). NullString only has 2 levels of indirection and takes up less space in memory, which is why it's used in database/sql. The Null... types also enjoy official driver support and will be targeted in future improvements.

The coupling is unfortunate, but unavoidable now.
Still, it really shouldn't influence the design of structs as those can't be used in Scan anyway.

@carbocation
Copy link
Author

Not sure if this is what you're referring to, but struct properties can be used in scan, e.g., Scan(&user.Id, &user.Handle, &user.Email, &user.Password, &user.Created) where user is an instance of a struct with those exported properties. I use them in some old code here (tightly coupled in its own way...) https://github.com/carbocation/go.user/blob/master/user.go .

@arnehormann
Copy link
Contributor

Yes, but you can't use the struct directly. You don't pass the struct, you have to pass references to each field.
So it's entirely possible to wrap it in a NullString beforehand. Tedious, but possible.

@xaprb
Copy link
Contributor

xaprb commented Dec 5, 2014

I'm not sure I follow this. Consider:

type foo struct {
   bar *string
   baz *int
}
f := foo{}
// ...
err = rows.Scan(f.bar, f.baz)

If the first column is null, isn't it going to throw an error "can't scan null into a string" as usual? Are you suggesting a way to work around this somehow?

@arnehormann
Copy link
Contributor

@xaprb don't pass pointers that way - they are nil on initialisation and copied by value, so you can't set the value from Scan anymore because Scan only sees nil. Use references to the fields.

And - as I said - it's tedious. See below.

type foo struct {
   bar *string
   baz int
}
f := foo{}
ns := sql.NullString{}
// ...
err = rows.Scan(&ns, &f.baz)
// ... error handling
if ns.Valid {
    *f.bar = ns.String
}

@xaprb
Copy link
Contributor

xaprb commented Dec 7, 2014

True. Although strictly speaking, if f.bar is an empty string before the scan (as it is in your sample code) you can just ignore the if ns.Valid and do the assignment, since if ns.Valid was false, you'd still have an empty string in ns.String anyway.

I'll close this now... it's one for the history books, right? Reopen if I'm wrong.

@xaprb xaprb closed this as completed Dec 7, 2014
@arnehormann
Copy link
Contributor

You are right - and let's keep it closed.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants