Cobra does 'nil tracking' which forces specification of variables that may be allowed to contain nil.
This is done to
- Help eliminate runtime exceptions about nil reference errors
- Allow reporting of multiple nil referencing errors at compile time (instead of a single failure point at runtime)
- Help document which parameters permit nil and which do not
nil tracking is done by typing the variable as a nilable Type which is by typing to any typeName with a ? suffix
str as String = "Mystring" # str can only have a String value strN as String? # strN may have a String value OR nil lazyType as Type? # Type OR nil
nilable Typing can be applied to anything that can be Typed ( local variables, class variables Args and returnTypes)
def sendWelcome(cust as Customer, referral as Customer?) # will not accept nil for `cust` # will accept nil for `referral`
Casting to and From nilable Types
Sometimes it is necessary to cast an existing typed variable to a nilable type
( e.g for receiving results of a library call) or
to cast a nilable type to its non nilable type (when you've ascertained its not nil and the receiver only takes a non nilable type)
There is a shorthand for both of these
Casting to a nilable version of an existing type
<variable> to ?
Casting away from nilable version
<variable> to !
s as String = 'a string' sn = s to ? # sn is String? assert sn s1 = sn to ! # s1 is String - may not get nil
nil and not nil detection
You can explicitly test for nil or not nil values using normal conditionals (true is non-nil)
s as String = 'sss' assert s if s print 's not nil' sn as String? = nil assert not sn if not sn print 'sn is nil'
nil and non-nil coalesce
There are shortcuts for Nil test and assign default (coalesce) or non nil test and process
a ? b coalesce to b if (a) nil.
The expression evaluates to a unless that value is nil, in which case, it evaluates to b.
Neither expression will be evaluated more than once and if the first expression is non-nil then b will not be evaluated at all.
( Also the direct augmented assignment version a ?= b a gets b if a is nil )
a ! b evaluate b if a not nil.
stuff as String? = nil defaultStuff='DEFAULT' stuff = stuff ? defaultStuff # coalesce nil # OR stuff ?= defaultStuff # equivalent to stuff = if (not stuff, defaultStuff, stuff) # or # if not stuff # stuff = defaultStuff assert stuff == defaultStuff def doSomething(input as String) if input # detect not nil print 'Received some input.' i as int? = input ! .parse(input) # execute non-nil # equiv to # i = if(input, .parse(input), input) # an alternative would also be: # input = input ? '0' # i as int = .parse(input)