Albert asked:
>> What's the thinking behind only doing defer on function returns, as
>> opposed to also doing it on for-loop "returns"?
Your reply doesn't answer his question, and declaring the file
at function scope wouldn't help in his example -- there are,
presumably, lots of files, one per iteration. A single defer
won't close them all.
Chris
--
Chris "allusive" Dollin
The /implementation/ of defer is a clever trick, but that's not its
definition, nor (I suspect) the reason that defer functions get
called on function exit.
like this:
func f() {
if debugging {
fmt.Printf("inside f\n")
defer fmt.Printf("leaving f\n")
}
bodyOfFunction()
}
or this:
func f(out io.Writer) {
if out == nil {
out, _ = os.Open("/tmp/log", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
defer out.Close()
}
fmt.Fprintf(out, "output of f\n")
}
if you want a scope-defined defer, you can always break out the
code into a separate function, or even use an inline function:
for {
func(){
file, err := os.Open("/dev/null", os.O_RDONLY, 0)
defer file.Close()
// ...
}()
> What's the thinking behind only doing defer on function returns, as
> opposed to also doing it on for-loop "returns"?
There are times when it is useful to call defer in a loop. It's
straightforward to make your loop call a function if you want a defer to
run at each loop iteration. The reverse--providing a way to call defer
in a loop if all defer functions run each loop iteration--would be
difficult.
For example, here is a use of defer in a loop to implement a postorder
traversal of a binary tree without using explicit recursion (in effect
the recursion is exported to the defer stack).
func Postorder(t Tree, f func(Tree)) {
for t != nil {
defer f(t)
defer PostOrder(t.Left, f)
t = t.Right
}
}
Ian
Defer is a feature of the language. It could be implemented in
various different ways. That it has a neat implementation in the
*g family of compilers is nice, but it's not the /reason/ that defer
is in the language.
(As far as I know: refutations welcome ...)
> I won't ask for more than the necessities.
We all ask for more than "the necessities", because the
necessities are so bare. (Subtraction? Functions? Long names?
Nested arithmetic expressions? If statements, given that we have
switch?)
> Anyway, any block can be put into a function call and use defer,
> doesn't it?
That's a reason why having for-scoped defers isn't /necessary/,
but it's not necessarily a reason for them being /omitted/. After
all, having to wrap block B into (func() {B})() isn't exactly
transparent.
for {
file, err := os.Open("/dev/null", os.O_RDONLY, 0)
// ...
file.Close()
}
If you need old files, then you need a counter and a way to access it
anyways, which means that you just close the oldest one.
for {
oldFile = file // Can be expanded to include any number of files
file, err = os.Open("/dev/null", os.O_RDONLY, 0)
// ...
oldFile.Close()
}
If you need these files until the function ends, use a defer. And, if
the for loop has tons of paths and you don't know where it's going to
pop out and a defer would be useful, drop it into a function and use
defer.