Skip to content

Instantly share code, notes, and snippets.

@lamvak
Created May 11, 2012 00:11
Show Gist options
  • Save lamvak/2656724 to your computer and use it in GitHub Desktop.
Save lamvak/2656724 to your computer and use it in GitHub Desktop.
particularly disturbing fragment of my new poliqarp binding (in progress)
func MakeDict(prefix string, strings []string) (dict *Dict, err error) {
image_file_size := 0
dict, err = nil, nil
for _, s := range strings {
// accomodate the string itself, length counter and zero-byte marker
// for the reason of backwards debility
image_file_size += len(s) + 5
}
image_file, err := os.Create(prefix + `.image`)
defer image_file.Close()
if err != nil {
return
}
err = image_file.Truncate(int64(image_file_size))
if err != nil {
return
}
offset_file, err := os.Create(prefix + `.offset`)
defer offset_file.Close()
if err != nil {
return
}
err = offset_file.Truncate(int64(len(strings) * 4))
if err != nil {
return
}
var (
image []byte
raw_offset []byte
offset []uint32
image_tip uint32 = 0
)
image, err = MapFileFW(image_file)
if err != nil {
return
}
raw_offset, err = MapFileFW(offset_file)
if err != nil {
return
}
offset, err = Reslice32(raw_offset)
if err != nil {
return
}
for i, s := range strings {
err = Put32(image, uint64(image_tip), uint32(len(s)+1))
if err != nil {
Unmap(image)
Unmap(raw_offset)
return nil, err
}
image_tip += 4
offset[i] = image_tip
copy(image[image_tip:], s)
image_tip += uint32(len(s) + 1)
image[image_tip-1] = 0
}
SyncMap(image)
SyncMap(raw_offset)
return &Dict{Image: image, Offset: offset}, err //should be nil?
}
@dchest
Copy link

dchest commented May 11, 2012

image_file, err := os.Create(prefix + `.image`)
defer image_file.Close()
if err != nil {
    return
}

You might want to do defer x.Close() after error handling. If os.Create returns error and image_file is nil, you'll call Close() with nil receiver. It's not an error, because Close() handles this case, but still not a good practice.

imageFile, err := os.Create(prefix + `.image`)
if err != nil {
    return
}
defer imageFile.Close()

@lamvak
Copy link
Author

lamvak commented May 11, 2012

Well, my primary issue with this code was the ladder of
if err != nil {
return
}
For all I remember from c-ish languages closing an zero-fd is, as you mention, not an error. Thus doing so would be a defensive programming and in any case it was simpler than check what kind of errors may I expect from go's os.Create. I'm not yet convinced why is that not a good practice?
I would like to figure out some better way of handling errors for go, but not using panic (which is not exactly handling an error). I would like a scheme for error handling such that will reduce this ladder for a (optimally) fixed number of additional lines per function, but on the other hand would still support early error detection. Also, here I handle all errors the same way, but it would be best if one could inscribe some particular additional functionality to handle some errors in one particular fragment of such ladder.
Partially something like Haskell's Maybe type could be a solution, but for one thing - go has no type polymorphism (only interfaces).

@dchest
Copy link

dchest commented May 11, 2012

Sorry, I forgot to mention that my message was kind of offtopic, unrelated to your original question on mailing list about error handling. Just a nit about the order of error checking vs defer x.Close().

Note that zero fd and nil *os.File are two different things, but yes, as I said, os.File's Close() handles both conditions: http://golang.org/src/pkg/os/file_unix.go#L92

However, when dealing, for example, with zip package, it's not the case, and this code:

zipFile, err := zip.OpenReader("filename.zip")
defer zipFile.Close()
if err != nil {
      return err
}

will throw runtime error if there's an error opening zip file, because zipFile.Close() is deferred before error checking and zipFile is nil, so the correct code is:

zipFile, err := zip.OpenReader("filename.zip")
if err != nil {
      return err
}
defer zipFile.Close()

@lamvak
Copy link
Author

lamvak commented May 16, 2012

The question is may there be an error in os.Open or os.Create which would leave us with an open fd?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment