-
-
Save lamvak/2656724 to your computer and use it in GitHub Desktop.
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? | |
} |
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).
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()
The question is may there be an error in os.Open or os.Create which would leave us with an open fd?
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.