Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift Error: Binary operator '&&' cannot be applied to two 'Bool' operands

Tags:

swift

There obviously has to be a way to AND or OR multiple booleans.

I'm getting this error trying to write a function like this...

func isLocationWithinView(location: CGPoint, view: UIView) {
    //        return (location.x >= CGRectGetMinX(view.frame)) && (location.x <= CGRectGetMaxX(view.frame)) && (location.y >= CGRectGetMinY(view.frame)) && (location.y <= CGRectGetMaxY(view.frame))
    var a = true
    var b = false
    return a && b // Error: Binary operator '&&' cannot be applied to two 'Bool' operands
}

What's the solution to this?

like image 528
chris P Avatar asked Jan 23 '16 18:01

chris P


2 Answers

The error is misleading: the core is that you're missing return type ... -> Bool in your function signature, hence attempting to assign a boolean value to the empty tuple type () (with no explicit return type, the function expects returns to be of empty tuple type ()).

You can reproduce this misleading error for any attempt to assign a boolean value to a non-boolean type, where the boolean value is a result of a logical AND/OR expression being performed in the same expression as the invalid assignment:

var a : () = (true && false)    /* same error */
var b : Int = (true && false)   /* same error */
var c : () = (true || false)    /* same error (for binary op. '||') */

Whereas if you wrap your AND/OR operations in a closure or simply assign them to an intermediate boolean variable, you loose the obfuscated error message and is presented with the actual error.

var d : () = { _ -> Bool in return (true && false) }()
    /* Cannot convert call result type 'Bool' to expected type '()' */
var e = true && false
var f : () = e
    /* Cannot convert value of type 'Bool' to expected type '()' */

Now as to why you're given this misleading error. Both logical operators && and || are implemented with a conditional evaluation of their right hand side expressions (rhs), so that the rhs can be lazily evaluated only in case the left hand side (lhs) evaluates to true/false for &&/|| operators, respectively.

/* e.g. the AND '&&' logical binary infix operator */
func &&(lhs: BooleanType, @autoclosure rhs: () -> BooleanType) -> Bool {
    return lhs.boolValue ? rhs().boolValue : false
}

Since the lhs itself is invalid for the assignment that follows, possibly the lazy closure rhs throws an error incurred by the "external" invalid assignment from Bool type to (), but the error thrown ("Binary op '&&' cannot be applied ...") is not the actual source of the failure of && call.

To verify, we can implement our own non-lazy AND operator, say &&&, and, as expected, we don't receive the same obfuscate error:

infix operator &&& {
    associativity right
    precedence 120
}
func &&&(lhs: BooleanType, rhs: BooleanType) -> Bool {
    return lhs.boolValue ? rhs.boolValue : false
}
var g : () = false &&& true
/* Cannot convert value of type 'Bool' to expected type '()' */
like image 184
dfrib Avatar answered Oct 23 '22 19:10

dfrib


While the other answer has some really interesting points, in this particular case, adding a return type to your function solves it.

func isLocationWithinView(location: CGPoint, view: UIView) -> Bool {
    let a = true
    let b = false
    return a && b // Error: Binary operator '&&' cannot be applied to two 'Bool' operands
}

This would return true if a were true, and if not, it would look to b (lazy evaluation).

like image 2
Dan Rosenstark Avatar answered Oct 23 '22 18:10

Dan Rosenstark