return , a built-in Tcl command, returns a value, a code and other options from a particular level.
Return provides a code, at value, and a dictionary of settings at the indicated level, which the interpreter then processes at that level, taking action according to the provided code. The most common use case is to return a value from the current procedure:
proc knightswhosay {} { return Ni! }
This could be read as, "At the next level up, the code is ok, and the value Ni!."
-level is a positive integer indicating the number of levels up from the current level. Level 0 is the current level (the evaluation of return itself), level 1 is the caller of return, and so on. The default value is 1.
-code is either a positive integer or the symbolic value as specified later on this page. By default, the value of -code is 1 (ok). The following three commands all have the same effect: At level 1 (the caller of return) return with a status of 0 (success):
return return -level 0 -code return return -level 1 -code ok
The return value is either the final argument or the empty string. catch and try can be used to intercept a return.
Any additional option/value pairs are added to the options dictionary for the level.
When try or catch store the return options dictionary at some level, they adjust the -level and -code values so that the options dictionary represents the semantics of the options at that particular level. In other words, those entries do not contain the values that were provided to return, but instead contain the values that have the same meaning when viewed from the level that inspects the dictionary.
For example:
proc p1 {} { while 1 { catch p2 cres copts dict update copts -level level -code code {} puts [list -level $level -code $code] ;# -> -level 0 -code 0 return -options $copts $cres } } proc p2 {} { # These are also the default values for -level and -code return -level 1 -code ok hello } p1
This also means that returning an options dictionary obtained via catch try does not always cause the current script to return. For example, the following is an infinite loop:
proc p1 {} { while 1 { catch p2 cres copts dict update copts -level level -code code {} puts [list -level $level -code $code] ;# -> -level 0 -code 0 return -options $copts $cres } } proc p2 {} { return hello } p1
-code may be any of the following values:
-code is rarely used, as commands such as error, break and continue handle the common cases. However, a routine that implements a new control structure that acts like break, continue, or error might use return -level 1 .... Another use case for -code is to have the caller, of the current procedure, report that the wrong number of arguments were provided to the current procedure, since this makes the error message more clear. In this case, use return -level 1 -code error.
-errorinfo specifies an initial stack trace for $errorInfo. If it is not specified, the stack trace left in $errorInfo includes the call to the current routine and higher levels on the stack but does not include any information about the context of the error within the procedure. Typically the info is derived from the value left in $errorInfo when catch traps an error within the procedure.
If -errorcode is specified, list provides a value for $errorCode. Otherwise, $errorCode defaults default to NONE. (from: Tcl Help)
Text occurring in a script after return is never evaluated, and thus doesn't even have to be valid Tcl:
proc foo {} { puts Foo return This is not Tcl - code after the return is never evaluated so may be used for commenting... } ;# RS
DGP: In Tcl 7 and in recent enough Tcl 8.5 that is correct. In the releases in between, due to some limitations in the bytecode compiler/execution machinery it could not be just any text:
Joe English also responds that not just any arbitrary text can appear after return. proc interprets its third argument as a script. It's therefore unwise, and one could argue even illegal, to pass in something that's not at least syntactically valid as a script, even if you know that parts of it will never be executed. By way of analogy: lindex interprets its first argument as a list, so you'd better only pass it valid lists. In Tcl 7.6 and earlier you could actually get away with things like
lindex "a b c {bad{list" 1
as long as the examined part of the list was syntactically valid. However, this was more of an accidental artifact of implementation details than anything guaranteed by the language, and in fact this raises an error in more recent Tcl versions. Similarly, if a command expects a script, you'd better pass it a script.
PYK 2013-12-10: However, if lindex is missing its second value, the first value can still be any value, even one that isn't a valid list.
AMG: There's a reason for this. When [lindex] is given only one argument, it interprets that as an instruction to not perform any list indexing. No list indexing means no requirement to be a list. It's that simple.
jenglish's statement is correct, though it's more philosophical than practical. If [return] is redefined, code following the [return] could certainly come into play. Or perhaps the code isn't being executed but rather is being analyzed by Nagelfar, which will surely take issue with the invalidity of the code. One real possibility which would upset Tcl in any event is if the text following [return] contains mismatched braces.
And last, I really ought to mention that the first time Tcl runs the proc, it tries to bytecode the whole thing, which means wasting time analyzing garbage. If there's any doubt about the finality of the [return] (e.g. a conditional is in play), the compiled proc will contain code to return any errors found in parsing.
proc moo x { if {$x} {return 5} "invalid"asdf } % tcl::unsupported::disassemble proc moo ByteCode 0x0x913eb0, refCt 1, epoch 15, interp 0x0x8c6ac0 (epoch 15) Source "\nif {$x} {return 5}\n\"invalid\"a"... Cmds 2, src 34, inst 26, litObjs 4, aux 0, stkDepth 2, code/src 0.00 Proc 0x0x95b5e0, refCt 1, args 1, compiled locals 1 slot 0, scalar, arg, "x" Commands 2: 1: pc 0-11, src 1-18 2: pc 4-6, src 10-17 Command 1: "if {$x} {return 5}"... (0) loadScalar1 %v0 # var "x" (2) jumpFalse1 +7 # pc 9 Command 2: "return 5"... (4) push1 0 # "5" (6) done (7) nop (8) nop (9) push1 1 # "" (11) pop (12) push1 2 # "extra characters after close-quote" (14) push1 3 # "-code 1 -level 0 -errorcode NONE -errori"... (16) syntax +1 0 (25) done
The fact that return also terminates source can be used for loading array contents without specifying an array name. Let the file t.tcl contain:
return { one 1 two 2 three 3 }
Then you can write it like this:
array set myArrayName [source t.tcl] ;# RS
wdb: This works, but being a purist, I prefer this text in the file to source:
list one 1 two 2 three 3
RS 2006-06-23: sure. Just if you have hundreds and thousands of array elements, with list you'd have to backslash-escape the newlines, while with bracing they need not.
DKF: I'm of the kind of purist which doesn't insist on using list to indicate listishness; that's an illusion and the list-compiler knows it.
See package index script interface guidelines for another use of return in sourced scripts: The main use for return outside procedures is in pkgIndex.tcl:
if {![package vsatisfies [package provide Tcl] 8.4]} return
which avoids presenting the package to interps that cannot use it.
RS 2005-08-08: Using return -code error in place of plain error, you get a leaner error traceback which is possibly better to read:
% proc 1 x {if {$x<=0} {error "too small"}} % proc 2 x {if {$x<=0} {return -code error "too small"}} % 1 0 too small % set errorInfo too small while executing "error "too small"" (procedure "1" line 1) invoked from within "1 0" % 2 0 too small % set errorInfo too small while executing "2 0"
DKF: I find this is useful when distinguishing between problems with the values passed in by the caller (such as if the code really wants a string of length 37, has documented this, and yet the user has given a string of length 28), and problems internal to the code that it isn't reasonable for the caller to try to get right.
AMG PYK: return -level 0 $x simply sets the interpreter result to $x. This is the canonical identity function; see the linked page for more information.
HaO: When a return code should be forwarded to the caller, one could remove the level 0 to not directly trigger an eventual exception here:
if {[catch {script goes here} err options]} { dict unset options -level return -options $options }
This was useful to me in the context of Tk bind scripts, which return break if no further bind scripts should process.
PYK 2023-10-30: The example the routine is acting as return itself, and the standard way to do that is to increment the level:
if {[catch {script goes here} err options]} { dict incr options -level return -options $options }
PYK 2016-09-15: Historically, return at the end of a procedure was slightly more performant than a final command that didn't explicitly return. This isn't the case in modern versions of Tcl, where procedures are byte-compiled.
tailcall is also related to return in that it terminates execution of the current proc. However, unlike return, tailcall's continuation is not the caller. yieldTo is also related to tailcall in that it has a custom continuation.