Swift Optional Inout Parameters and Nil

Swift optional inout parameters and nil

It won't compile because the function expecting a reference but you passed nil. The problem have nothing to do with optional.

By declaring parameter with inout means that you will assign some value to it inside the function body. How can it assign value to nil?

You need to call it like

var a : MyClass? = nil
testFunc(&a) // value of a can be changed inside the function

If you know C++, this is C++ version of your code without optional

struct MyClass {};    
void testFunc(MyClass &p) {}
int main () { testFunc(nullptr); }

and you have this error message

main.cpp:6:6: note: candidate function not viable: no known conversion from 'nullptr_t' to 'MyClass &' for 1st argument

which is kind of equivalent to the on you got (but easier to understand)

swift How to send &nil to func?

You cannot use a value as an inout parameter in Swift. You need to create a variable of type CAShapeLayer, assing it a nil value and use that as the input parameter to your function.

func setupCircular(circularLayer:inout CAShapeLayer?){
if (circularLayer == nil) {
circularLayer = CAShapeLayer()
}else{

}
}

var circ: CAShapeLayer? = nil
setupCircular(circularLayer: &circ)

How to guarantee that inout argument won't change Type and won't become nil in a function

inout won't accept Generic type , because the reference wants to store value backs to its original value. here is the link for inout https://docs.swift.org/swift-book/LanguageGuide/Functions.html

Need help in using Swift inout with optional

@MartinR makes a good point in the comments: "Why does merge() take inout parameters? The arguments are not modified, so this seems like an unnecessary complication."

The reason for the error is that you have to pass the exact type when using inout, and DNode and DNode? are two different types.
newNode needs to be declared as optional because that is what merge is expecting:

func start(inout newNode: DNode?) {
self.minDNode = merge(&self.minDNode, And: &newNode) // this call now works
}

You probably should rework your code, though, to remove the inout calls where they aren't needed.

inout param with empty object since nil won't work

There are many things that won’t work here.

You need to give more than [] to declare an array. You need to give some indication of it’s element type. Either in the brackets e.g. [Int]() or from other context i.e. if test took an [Int] arg not an Any. Otherwise the compiler has no idea what type you mean by [].

You cannot pass literals (for example, nil) into an inout parameter. In fact you can’t pass any kind of immutable value, including temporary objects like [Int](), or variables declared with let. The only things you can pass to an inout parameter are “lvalues” – that is, things that can be on the left-hand side of an assignment. That means even if you changed this to `test(&Int) it won’t work.

Finally, even if you declare var a = [Int]() and then try to call test(&a) you’ll get an error, because the implicit copy of a from an array to an Any will result in another immutable temporary, which also can’t be passed as an inout.

Default optional parameter in Swift function

Optionals and default parameters are two different things.

An Optional is a variable that can be nil, that's it.

Default parameters use a default value when you omit that parameter, this default value is specified like this: func test(param: Int = 0)

If you specify a parameter that is an optional, you have to provide it, even if the value you want to pass is nil. If your function looks like this func test(param: Int?), you can't call it like this test(). Even though the parameter is optional, it doesn't have a default value.

You can also combine the two and have a parameter that takes an optional where nil is the default value, like this: func test(param: Int? = nil).

Is there a way to use a Template, and In-Out Parameter and Optional together?

The main problem here is that the generic T can never itself know if it is of Optional type or not, which makes successful type conversion to T tricky for the cases when T is in fact of type Optional<SomeType>. We could, ourselves, assert that T is an optional type (checking Mirror(reflecting: ...).displayStyle == .Optional etc), however this still doesn't solve conversion to T right off the bat. Instead, we could use another approach, as follows below.

We can work around the problem by creating two readData(...) functions, one taking an optional generic inout parameter, type U?, and the other one taking an implicitly non-optional generic inout parameter U (called only if U? function cannot be used, hence implicitly only called for non-optionals). These two functions, in turn, are minimal and basically only calls your "core" dataReader(..) function, where we've made the adjustment that the inout generic parameter is now explicitly optional, i.e., T?.

enum ConvertError: ErrorType {
case MissingParameter
case WrongType
}

/* optional inout parameter */
func readData<U>(inout output: U?, _ input: AnyObject?, _ throwError: Bool = true) throws {
try readDataCore(&output, input, throwError)
}

/* non-optional inout parameter */
func readData<U>(inout output: U, _ input: AnyObject?, _ throwError: Bool = true) throws {
var outputOpt : U? = output
try readDataCore(&outputOpt, input, throwError)
output = outputOpt!
/* you could use a guard-throw here for the unwrapping of 'outputOpt', but
note that 'outputOpt' is initialized with a non-nil value, and that it can
never become 'nil' in readDataHelper; so "safe" forced unwrapping here. */
}

/* "core" function */
func readDataCore<T>(inout output: T?, _ input: AnyObject?, _ throwError: Bool = true) throws
{
if (input == nil) {
if (throwError) {
throw ConvertError.MissingParameter
}
}
else {
if let inputObject: T = input as? T {
output = inputObject
}
else if (throwError) {
throw ConvertError.WrongType
}
}
}

As we try this out, we see that we now get the behaviour we're looking for, even if the argument sent as inout parameter is nil or Optional.


Example 1: using optional inout param with value nil

class Properties
{
var x:Int?
var y:Int?
}

var myJson : [String:Int] = ["data":10]
var originalProperties = Properties()

do {
try readData(&originalProperties.x, myJson["data"], true)
print("originalProperties.x = \(originalProperties.x ?? 0)")
} catch ConvertError.MissingParameter {
print("Missing parameter")
} catch ConvertError.WrongType {
print("Wrong type")
} catch {
print("Unknown error")
}
/* Prints: 'originalProperties.x = 10', ok! */

// try some non-existing key 'foo'
do {
try readData(&originalProperties.x, myJson["foo"], true)
print("originalProperties.x = \(originalProperties.x ?? 0)")
} catch ConvertError.MissingParameter {
print("Missing parameter")
} catch ConvertError.WrongType {
print("Wrong type")
} catch {
print("Unknown error")
}
/* Prints: 'Missing parameter', ok! */

Example 2: using optional inout param with, however, with a non-optional value

class Properties
{
var x:Int? = 1
var y:Int? = 1
}

var myJson : [String:Int] = ["data":10]
var originalProperties = Properties()

// ...

/* Same results as for Example 1:
'originalProperties.x = 10' and 'Missing parameter' */

Example 3: using non-optional inout parameter

class Properties
{
var x:Int = 1
var y:Int = 1
}

var myJson : [String:Int] = ["data":10]
var originalProperties = Properties()

// ...

/* Same results as for Example 1:
'originalProperties.x = 10' and 'Missing parameter' */

NULL parameter in Swift

Your Objective-C method has nullable pointers as parameters,
in Swift 3 that would be an optional UnsafeMutablePointer:

func getRect(aRectRef: UnsafeMutablePointer<CGRect>?, bRectRef: UnsafeMutablePointer<CGRect>?) {
if let aRectPtr = aRectRef {
aRectPtr.pointee = CGRect(x: 1, y: 2, width: 3, height: 4)
}
if let bRectPtr = bRectRef {
bRectPtr.pointee = CGRect(x: 5, y: 6, width: 7, height: 8)
}
}

var rect = CGRect.zero
getRect(aRectRef: &rect, bRectRef: nil)
print(rect) // (1.0, 2.0, 3.0, 4.0)

So you can pass nil as an argument. What you can not do
(in contrast to Objective-C) is to pass the address of an uninitialized variable, rect must be initialized here.

The same can be written more compactly as

func getRect(aRectRef: UnsafeMutablePointer<CGRect>?, bRectRef: UnsafeMutablePointer<CGRect>?) {

aRectRef.map { $0.pointee = CGRect(x: 1, y: 2, width: 3, height: 4) }
bRectRef.map { $0.pointee = CGRect(x: 5, y: 6, width: 7, height: 8) }
}

Swift 4: Escaping closures can only capture inout parameters explicitly by value

The error is described in detail in this answer.

The problem with your code is that the first closure

fileprivate func fetchUserAvatar(_ internalUrl : URL, externalUrl : URL,_ task : inout URLSessionTask?, completion : @escaping (_ image : UIImage?) -> ()) {
fetchImage(externalUrl, task: &task, completion: { image in // <-- HERE --
if image == nil {

is an escaping closure. So when the code

        if image == nil {
self.fetchImage(internalUrl, task: &task, completion: completion) // <-- HERE --
} else {

tries to write to the task variable, the original fetchUserAvatar call has already completed.

Note: I have written comments like this <-- HERE -- into the snippets, to clarify which line I am talking about. Also, please make sure to check out the answer that I linked above, because it will clarify everything..

The bad news is that you will have to refactor the code a bit to fix the error. You'll need to change the signatures of both fetchUserThumbnailAvatar, as well as fetchUserAvatar for that, and that will break callers; so the callers have to be changed as well. Therefore I cannot fix it for you, because the fix depends on code that I don't have.



Related Topics



Leave a reply



Submit