Swift
Integers in Swift are ok. They cannot be infinite like Doubles, thought. They have a limit.
Surpassing that limit will cause a crash.
Exhibit A
Int(10000000000000000000)
error: integer literal '10000000000000000000' overflows when stored into 'Int'
Exhibit B
Int(pow(Double(1000000000), Double(10)))
Fatal error: Double value cannot be converted to Int because the result would be greater than Int.max
I naively thought to myself, "Hey, this is a fatal error. Can I catch the error with a do, catch block?"
Nope
Exhibit C
do {
Int(pow(Double(1000000000), Double(10)))
} catch {
print("safety net")
}
print("good?")
warning: 'catch' block is unreachable because no errors are thrown in 'do' block
Fatal error: Double value cannot be converted to Int because the result would be greater than Int.max
Oh, yeah. That's right! I forgot to add try
Nope
Exhibit D
do {
try Int(pow(Double(1000000000), Double(10)))
} catch {
print("safety net")
}
print("good?")
warning: no calls to throwing functions occur within 'try' expression
warning: 'catch' block is unreachable because no errors are thrown in 'do' block
Fatal error: Double value cannot be converted to Int because the result would be greater than Int.max
What is going on?
Can anyone explain this to me? I would really like to be able to catch an error like this.
Thank you so much, this would be a huge help!
You can use the init(exactly:) constructor, it will not throw an error but it will return nil if the value is to large
guard let value = Int(exactly: pow(Double(1000000000), Double(10))) else {
//error handling
}
Related
I'm trying to put a very simple unitTest-Like function together for teaching Swift, when we're not quite ready for the full XCTest framework.
I've put together this code:
enum TestFailure : Error {
case testEqFailure(_ msg: String)
}
func testEq<E:Equatable>(_ actual: E, _ expected: E ) throws {
if actual == expected { return }
print("Test failed")
print("Actual Result : \(actual)")
print("Expected Result: \(expected)")
throw TestFailure.testEqFailure("Test Values were not equal")
}
try testEq(1,1)
try testEq(7,8)
It "works" just fine (as shown by the output):
Test failed
Actual Result : 7
Expected Result: 8
Swift/ErrorType.swift:200: Fatal error: Error raised at top level: vPlay.TestFailure.testEqFailure("Test Values were not equal")
2021-11-01 10:36:52.050106-0400 vPlay[49261:2400984] Swift/ErrorType.swift:200: Fatal error: Error raised at top level: vPlay.TestFailure.testEqFailure("Test Values were not equal")
but when I run it, Xcode is highlighting the wrong source line:
What am I missing? (BTW, this is being used in the top level code of a command line program to keep things simple and focus on the actual coding)
Try calling the function with a do catch instead
try testEq(1,1)
do {
try testEq(7,8)
} catch {
print(error)
}
The error I get in playground is "An error was thrown and was not caught:" but not pointing to the first function call rather the second function call.
When you have a block of code that contains a method which can potentially throw an error you need to call the function with a do catch statement.
When the throwing method fails and raises the error the execution will go into the catch block and be handled according to what you dictate within the catch block.
The first function call is not failing because it doesn't throw an error but if it is for whatever IDE your using then try a do catch on that call as well.
If you want to teach a sudo assert test with a throwing function then you will have to teach error handling also or swift will fail when it has to handle the error being thrown.
You could eliminate the throwing and enum error if you want to simplify this for a beginner but still get the point across.
func testEq<E:Equatable>(_ actual: E, _ expected: E ) {
if actual == expected { return }
print("Test failed")
print("Actual Result : \(actual)")
print("Expected Result: \(expected)")
}
testEq(1,1)
testEq(7,8)
This will print what you want to see, actual and expected with the print test failed along side it to simulate a failure.
I am trying to implement a Fibonacci function in Swift 5. While I was implementing the function on Xcode Playground (I took the code for the function from CSDojo's youtube video), I was encountered with this error message
error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
Initially, I thought the error must come from subscript index out of range. So I checked my code again and it seems there is no issue. Now I wonder why I get this error message from Xcode Playground when I try to run computeFiboBottomUp(n: 100)? It works fine until computeFiboBottomUp(n:35).
Is it because there are too many recursive calls on the stack for the compiler to process? Or does my code possibly contain an error?
func computeFiboBottomUp(n: Int) -> Int {
var bottom_up: [Int?] = []
if bottom_up.isEmpty {
for _ in 0...n {
bottom_up.append(nil)
}
}
if n == 1 || n == 2 { return 1 }
bottom_up[1] = 1
bottom_up[2] = 1
for i in 3...n {
bottom_up[i] = bottom_up[i-1]! + bottom_up[i-2]!
}
return bottom_up[n]!
}
computeFiboBottomUp(n: 5)
computeFiboBottomUp(n: 35)
computeFiboBottomUp(n: 100) // error...
You have no recursion in your code.
Note that Fib(35) is too small for integer overflow, but Fib(100) definitely is too large both for 32-bit and for 64-bit integers, so your compiler catches integer overflow error.
You may set limit n=41 for 32-bit signed ints and n=92 for 64-bit signed ints.
Higher values require using of long/arbitrary precision integer arithmetics.
I looked for this article, but it does not cover my case.
If i understand correct, we can use try either in do..catch.. statement or in a function that can throw.
But sometimes i see something like:
let jsonData = try jsonEncoder.encode(employee1)
Where jsonData is not optional. What is meaning of this? What if try sttement fail? Why value is not optional? Can someone explain? Thanks.
In addition to the cases you mentioned, you can call try at
top-level code. Here is a simple self-contained example:
// main.swift:
enum MyError : Error {
case failed
}
func foo() throws -> Int {
throw MyError.failed
}
defer { print("Good bye.") }
let x = try foo()
print(x)
You can compile and run this as a Xcode "Command Line Project"
or directly from the command line:
$ swiftc main.swift
$ ./main
Good bye.
Fatal error: Error raised at top level: main.MyError.failed: file /BuildRoot/Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-900.0.74.1/src/swift/stdlib/public/core/ErrorType.swift, line 187
Illegal instruction: 4
The failed try in the top-level code causes the program to
terminate with an error message. Deferred statement (if present) will be executed however.
This is slightly different from using a forced try! statement,
which causes the program to abort as well, but immediately, without executing deferred statements. (This can be relevant if deferred
statements are used to clean-up resources, e.g. remove temporary files).
The error message originates from ErrorType.swift, line 187:
/// Invoked by the compiler when code at top level throws an uncaught error.
#_inlineable // FIXME(sil-serialize-all)
#_silgen_name("swift_errorInMain")
public func _errorInMain(_ error: Error) {
fatalError("Error raised at top level: \(String(reflecting: error))")
}
(also observed in Non Exhaustive List When Handling Errors Inside a Class Function in Swift).
Apparently the top-level code behaves as if embedded in a
do-catch block:
do {
func foo() throws -> Int {
throw NSError(domain: "foo", code: 0, userInfo: nil)
}
defer { print("Good bye.") }
let x = try foo()
} catch {
fatalError("Error raised at top level: \(String(reflecting: error))")
}
A function call marked with try like the one you posted, as you stated, must be located inside a function marked with the keyword throws or a do/catch statement or it will cause a compile error.
On a side note if you want to call a throwing function outside a do/catch or another throwing function you can use try? before your function call to get an optional result that will be nil if something is thrown. There is also the try! variant that assumes nothing will be thrown returning a non-optional result, but if something is thrown the app will crash.
In my code I'm deserialising some XML using the SWXMLHash library. Its .value() method has the throws keyword in its declaration, as do any custom deserialise functions.
I have the following line of code:
let myValue : UInt8 = try? xml["Root"]["ValueNode"].value()
Since the library doesn't include a deserialiser for UInt8, I've defined my own:
extension UInt8: XMLElementDeserializable {
public static func deserialize(_ element: XMLElement) throws -> UInt8 {
return UInt8(element.text)!
}
}
This works when the node has a value. However, when the node doesn't exist or is nil, an error occurs on the following line:
return UInt8(element.text)! // Fatal error: Unexpectedly found nil while unwrapping an Optional value
This is supposed to happen, obviously. What I don't understand is why this error is not being caught by my try? statement and returning nil instead of throwing that error.
Can anyone help?
Not all errors can be caught in Swift. If you mark a function using the throws keyword it indicates that the function might throw a recoverable error. However, your custom implementation doesn't actually throw any errors. Only errors thrown from functions marked with the throws keyword and thrown using the code throw Error can be caught by a do-catch block.
try? is a way to convert a throwable function's return value to an optional. If the function would throw an error, the value after the try? will be nil, otherwise it will be an optional value.
When you use the !, you specifically tell the compiler that you know what you are doing and if the operation on which you used the ! fails, your app shouldn't fail gracefully.
You'll need to change your deserialize method to handle the optional unwrapping gracefully or throw and error.
extension UInt8: XMLElementDeserializable {
public static func deserialize(_ element: XMLElement) throws -> UInt8 {
if let val = UInt8(element.text) {
return val
} else {
throw NSError(domain: "Couldn't deserialize value to UInt8", code: 1)
}
}
}
return UInt8(element.text)!
There's no try in this line. Therefore, no errors are going to be thrown here. If UInt8 can't convert the string it's given, it just returns nil. And then, of course, your ! turns that nil into a crash.
Instead of that, do something like this instead:
guard let retVal = UInt8(element.text) else { throw SomeError }
return retVal
In general: When there's a way to do something using !, and another way to do the same thing without using !, go for the second one unless you've got a really good reason.
I do not know how do I handle runtime error in swift. I need to check for a parsing error and do something about it. Can anyone help me please?
I have code something like this:
var:SomeObject = parse("some string")
I need to handle any generic error that occurs in runtime.
Thanks!
If the function is yours, then in case of a failure, you can make that function return a nil value.
That way your line of code
var anObj:SomeObject = parse("some string")
would become something like this
var:SomeObject? = parse("some string")
Notice the ? sign. It means that the value in it is Optional. In simple words, it could be some actual value, or it could be nil.
After this function, you should perform a check like
If anObj != nil
{
//do something
}
else
{
//the parse didn't go right, handle the erorr here.
}
Swift 2 adds additional safety to your error checking. You use the throws keyword to specify which functions and methods could throw an error. Then you have the do, try, and catch keywords for when you call something that could throw:
// 1
enum ParseError: ErrorType {
case InvalidValue
}
// 2
func parseWithError(value:String) throws {
if value.count > 0 {
// yeaaa!
} else {
// 3
throw ParseError.InvalidValue
}
}
func parse(value:String) {
// 4
do {
try parseWithError(value)
} catch {
print("Could not parse! :[")
return
}
}
There are a few things to highlight here:
To create an error to throw, simply create an enum that derives from ErrorType.
You need to use the throws keyword to mark any function that can throw an error.
This throws an error, which will be caught in section 4.
Instead of try blocks, which might be familiar from other languages, you wrap any code that can throw an error in a do block. Then, you add the try keyword to each function call that could throw an error.
For more read here or this is the official documentation of Error Handling