lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


It was thus said that the Great Paul Ducklin once stated:
> 
> It’s also not that clear how to use it, given that the manual says that
> to-be-closed variables are constants. My thought is that seems like a
> great feature for things like files and sockets as a way of recovering
> cleanly from errors - but I’ve never thought of files and sockets as
> “constant variables” because they are all about side effects and
> continuously changing state.

  In Lua, variables and values are two distinct things.  In Lua, I can do:

	x = 'one'
	x = 2
	x = { 'three' }
	x = io.stdout
	x = print

That's because a variable (in this case, 'x') is a name to which you can
attach a value---values have types,  not variables.  When you do:

	local <toclose> x = ...

  This makes the variable 'x' constant---you cannot change what value it
has, but the value itself may change (a table, a userdata, etc).

> In short: why do I need <toclose>, what do I do with it, and how do I use
> it properly?

  Given the following example code:

	local function readfile(name)
	  local <toclose> f = io.open(name)
	  if not f then return end
	  local res = {}
	  res.date = parse_date(f)
	  if not res.date then return end
	  res.name = parse_name(f)
	  if not res.name then return end
	  res.data = parse_data(f)
	  if not res.data then return end
	  return res
	end

  Each place where we return, the file f will be closed (because it's marked
as <toclose> and we're leaving the current scope).  There's nothing
inherenetly *wrong* if you don't have <toclose>, as eventually the file will
be closed when garbage collection kicks in, but that might be awhile, and in
the meantime, the number of open files you can have is one less, which could
cause an open failure down the road.  To solve this pre-Lua 5.4, you have to
explicitely close the file along each error path:

	local function readfile(name)
	  local f = io.open(name)
	  if not f then return end
	  local res = {}
	  res.date = parse_date(f)
	  if not res.date  then
	    f:close()
	    return
	  end
	  res.name = parse_name(f)
	  if not res.name then
	    f:close()
	    return
	  end
	  res.data = parse_data(f)
	  if not res.data  then
	    f:close()
	    return
	  end
	  f:close()
	  return res
	end

  Or perhaps this:
  
  	local function readfile(name)
  	  local function readdata(f,res)
  	    res.date = parse_date(f)
  	    if not res.date then return false end
  	    res.name = parse_name(f)
  	    if not res.name then return false end
  	    res.data = parse_data(f)
  	    return res.data ~= nil
  	  end
  	  
  	  local f = io.open(name)
  	  local res = {}
  	  local good = readdata(f,res)
  	  f:close()
  	  if good then return res end
  	end

  You generally want to use <toclose> for resources other than memory where
waiting for garbage collection may be too long.  And remember, this is a
contrived example.  Imaging two files being processed, or the function being
longer, or more possible error paths (or all the above).

  This if this as coroutines---you can go a very long time without having to
understand, or even use, coroutines, but yet they're there for when you need
them.

  -spc