4 ms·
>I think that piece would get too long if it described everything in depth. I don't think one sentence more about a very fundamental operation in a page-long d
by pingyong 7y ago
>I think that piece would get too long if it described everything in depth.
I don't think one sentence more about a very fundamental operation in a page-long document would really be misplaced. But as far as the language itself goes, those operators seem fine to me.
>Note that there is errdefer
That is definitely more useful, and covers the situation I was thinking of.
>I don't understand what you're asking about here. If you malloc() an array in C, then you free() it when you're done with it.
This is about composed objects. So an allocated array of objects, which in turn also hold allocated objects, which might in turn also hold allocated objects.
>I don't agree that it's violating the "no hidden control flow constraint." It's explicit, right there in the scope, not stashed away in a bunch of destructors. It's as transparent as return or goto or break.
Not sure I can completely agree with that. Fundamentally, defer means that something happens at a point where there is no associated code. If you read the line "return 5;", you don't know if that triggers one (or multiple) function calls. Yes, you only have to look for those in function scope (which could be 5000+ lines in some cases) (you only have to look for destructors in function scope too btw), but it is still a "hidden code path". For me personally it doesn't really matter at that point if I have to first search in the function body (and then potentially in the cleanup function), or first in the function body and then potentially in destructors. It removes the ability to reason about code line by line either way. Which might be a price that is worth to pay for easier cleanup logic, but the way I'm seeing it I'm only getting like a quarter of the benefit of destructors but I'm paying essentially the same price.
- clarry 7y ago> This is about composed objects. So an allocated array of objects, which in turn also hold allocated objects, which might in turn also hold allocated objects. The way this is solved by calling the cleanup routine for the first object in the hierarchy. If that object is responsible for the lifetime of other objects, then it will also call their cleanup routine. From the user's perspective, those are invisible, just as any calls to free() or close() or fflush() inside an fclose() are. > If you read the line "return 5;", you don't know if that triggers one (or multiple) function calls. If you read the line "continue;" or "break;", you don't know if that triggers one (or multiple) function calls until you read the surrounding code. I still find it much easier to follow code inside a function than to jump through classes. But the bigger picture is that it makes lifetime management explicit and thus transparent, and you needn't complicate the language with copy constructors and move semantics.
- pingyong 7y ago>If you read the line "continue;" or "break;", you don't know if that triggers one (or multiple) function calls until you read the surrounding code. Not sure what you mean exactly - you have to read the surrounding code to know where the control flow continues, but there's no hidden function call between that point and and the break. That's like arguing you need to understand the entire code base to know what return does - since it returns control flow to potentially a completely different file. But that's not really the point, the point is that it doesn't do anything else in between - if you know where control flow is going to, you know everything that is happening. More so with break and continue even, since for break and continue where control flow continues doesn't even depend on runtime state, whereas return could return to different places depending on where the function was called.
- clarry 7y agoWith break, control flow goes out of the loop or out of the switch. You may find function calls there. With continue, control flow goes to the start of the loop. You may find function calls there. With return, control goes through the defer blocks and then out of the function. Similar reasoning for goto. In every case, you know where the control flow goes by looking at the context inside the function. I don't agree with the assessment that one of them is more hidden than the other.
- pingyong 7y ago>With break, control flow goes out of the loop or out of the switch. You may find function calls there. Yeah but you don't have to look for anything in between. You look for where it goes, and that's it. That there could be a function call after the continue has executed isn't relevant at all, there could also be function call after a return has executed. Stepping through the code in your head is trivial because the execution never jumps anywhere without an explicit statement to jump. With defers, you need to make sure you find every single defer that was executed up to the return (which might not be trivial if defers are executed conditionally). With a break there is exactly one, unconditional, position at which execution continues - and if you have found it, there is no reason to keep looking anywhere else. I mean wanting to make the defer trade-off but not the destructor trade-off, fine, that is ultimately a matter of opinion. But defer being hidden control flow is just objectively true, execution jumps somewhere without a corresponding explicit jump in the code (return only explicitly jumps out of the function).