I'm getting a strange Bus Error when running what appears to be perfectly safe swift code. I've tried to reduce it down to a minimal test case, as follows:
Apple Swift version 2.2-dev (LLVM 3ebdbb2c7e, Clang f66c5bb67b, Swift 0ddf238ad7)
Target: x86_64-apple-macosx10.9
This code:
public enum MyError: ErrorType {
case SomeError(code: Int)
}
public typealias MyType = () throws -> Bool
public class Foo {
var a:MyType = { () throws -> Bool in
print("A")
return true
}
var b:MyType = { () throws -> Bool in
print("B")
return true
}
var c:MyType = { () throws -> Bool in
print("C")
throw MyError.SomeError(0)
}
}
public func handle<T>(test:T) {
let mirror = Mirror(reflecting: test)
print(mirror.subjectType)
for child in mirror.children {
if let callable = child.value as? MyType {
do {
try callable()
}
catch MyError.SomeError(let id) {
print(id)
}
catch {
print("unknown error")
}
}
}
}
let foo = Foo()
handle(foo)
Generates this output:
Foo
A
B
C
Bus error: 10
Running it in the debugger works fine, so I assume it has something to do with a timing issue at runtime.
Am I doing something illegal or unsafe in this code?
Are exceptions somehow illegal in closures?
What's causing this error?
Edit:
I've created a bug on the swift issue tracker for this now here: https://bugs.swift.org/browse/SR-324
What's causing this error?
The error doesn't happen until you get to the last closure:
var c:MyType = { () throws -> Bool in
print("C")
throw MyError.SomeError(0)
}
Obviously, you're throwing an exception here, and I suspect that the problem has less to do with iterating over the children and more to do with throwing an exception while you're doing that iterating. I tried calling c without iterating:
public func trythis() {
let foo = Foo()
do {
try (foo.c)()
}
catch MyError.SomeError(let id) {
print(id)
}
catch { print("unknown") }
}
trythis()
and found that it worked fine. I also tried removing the throw from c:
var c:MyType = { () throws -> Bool in
print("C")
// throw MyError.SomeError(code: 0)
return true
}
and found that the code works fine in that case. So it's the combination of throwing while iterating over the list that's the problem, and that makes me suspect that it's just a compiler bug or maybe some problem with the Mirror class.
I think you should file a bug report with Apple for this one.
I agree with Caleb that this must be a bug.
But just to be clear, it is not the combination of throwing while iterating. It is the combination of reflecting and throwing.
This is a modified version of your handle function:
public func handle<T>(test:T) {
let mirror = Mirror(reflecting: test)
print(mirror.subjectType)
// Extract only the last function, no iteration...
if let callable = mirror.children[AnyForwardIndex(2)].value as? MyType {
do {
try callable()
}
catch MyError.SomeError(let id) {
print(id)
}
catch {
print("unknown error")
}
}
}
This function will cause the same error as your function.
You simply can not call a function that throws, if found using reflection.
Bug I would say.
Related
I have this code in SWift:
import UIKit
import Foundation
func whatever() async -> Int{
return 2;
}
func test(){
Task.init{
var testing:Int = 0
do {
testing = try await whatever()
}
catch {
print("some error happened")
}
}
DispatchQueue.main.async {
print("from dispa")
if(testing == 0){
print("testing was never set")
}
}
}
test()
It is a playground and upon running it, I get Cannot find 'testing' in scope in the if statement in the DispatchQueue
What am I doing wrong? Moving the code within the Task.init throws a different error: Reference to captured var 'testing' in concurrently-executing code
The variable testing is declared inside the Task.init scope right now, meaning the DispatchQueue closure has no access to it. To use it in both closures, you must move it to a scope in which both closures have access to it (ie a shared parent scope).
Once you've done that, I'll say it's a little unclear on what exactly you want happening here, but I'm guessing this is the result you are looking for. Note that you should be careful about mixing Tasks and DispatchQueues -- they are different paradigms and do not necessarily lead to the results that you think they would.
var testing = 0
func whatever() async throws -> Int {
return 2 //note that nothing async or throwing is actually happening here
}
func test(){
Task {
do {
testing = try await whatever()
}
catch {
print("some error happened")
}
}
DispatchQueue.main.async {
print("from dispatch")
if testing == 0 {
print("testing was never set")
}
}
}
In the following example, the function usingTemporaryDirectory() creates and deletes a temporary directory, calling a passed function body() in between. If an exception is thrown by createTemporaryDirectory() or the passed function body(), it is propagated to the caller. But the exception thrown by removeDirectory() cannot be passed to the caller because no exception may escape a defer block.
import Foundation
func createTemporaryDirectory() throws -> URL { ... }
func removeDirectory(_ url: URL) throws { ... }
func usingTemporaryDirectory(body: (URL) throws -> ()) throws {
let tempDir = try createTemporaryDirectory()
defer {
// Errors thrown from here are not handled.
try removeDirectory(tempDir)
}
try body(tempDir)
}
What is the proper way to handle such an exception? I see two options:
Just catch the exception and log a message.
Catch the exception, log a message and abort the process.
I don't want to use option 1 because that could lead in this example to an arbitrary number of temporary directories piling up. And I also don't want to use option 2 because that would prevent outer stack frames from completing their cleanup work (e.g. if multiple temporary directories have been created, removal for all of them should at least be attempted).
Java has a feature called suppressed exceptions. In this case the exception thrown in the defer block could be added as a suppressed exception to the exception thrown by body(), if any. Does Swift have a comparable feature?
In this case the exception thrown in the defer block could be added as a suppressed exception to the exception thrown by body(), if any. Does Swift have a comparable feature?
Not that I know of – you could however build something similar yourself. First, let's define an Error type that can store multiple underlying errors:
struct ErrorCollection : Error {
private var errors: [Error] = []
init() {}
init<S : Sequence>(_ sequence: S) where S.Element == Error {
for error in sequence {
append(error)
}
}
mutating func append(_ error: Error) {
switch error {
case let x as ErrorCollection: // ensure we flatten out any nested error collections.
errors.append(contentsOf: x.errors)
case let x:
errors.append(x)
}
}
}
extension ErrorCollection : RandomAccessCollection {
typealias Index = Int
typealias Element = Error
var startIndex: Index { return errors.startIndex }
var endIndex: Index { return errors.endIndex }
func index(_ i: Index, offsetBy n: Index) -> Index {
return errors.index(i, offsetBy: n)
}
subscript(index: Index) -> Element { return errors[index] }
}
Then we can define a Result<T> type which we can use in order to evaluate a closure, storing the return value if successful, otherwise the error it threw. We can then add a then(_:) method to allow us to catch any additional errors, potentially causing the success value to be invalidated:
enum Result<T> {
case success(T)
case failure(Error)
init(_ body: () throws -> T) {
do {
self = .success(try body())
} catch {
self = .failure(error)
}
}
func then(_ body: () throws -> Void) -> Result {
do {
try body()
return self
} catch let nextError {
switch self {
case .success: // invalidate the success value and store the error.
return .failure(nextError)
case .failure(let error): // concatenate the errors.
return .failure(ErrorCollection([error, nextError]))
}
}
}
func materialize() throws -> T {
switch self {
case .success(let value):
return value
case .failure(let error):
throw error
}
}
}
You can then use this like so:
func usingTemporaryDirectory<R>(body: (URL) throws -> R) throws -> R {
let tempDir = try createTemporaryDirectory()
return try Result { try body(tempDir) }
.then { try removeDirectory(tempDir) }
.materialize()
}
I am working on an app and want to get data back from a function. However sometimes data is missing or is different from the kind of that I want to retrieve. I am new to Swift and I can't find a way to write a function that does a little bit of processing and returns this data. When this data is missing, the function should give back a string "Not Found". Like this:
func processData(data:String) {
do {
//processing
var result = processedData
} catch {
var result = "Not Found"
}
return result
}
It would be very nice if somebody could help me.
You should check if the result is nil.
func processData(data: String?) -> String {
guard let result = data else {
return "Not Found"
}
return result
}
The most concise way of doing it would be using the guard-let construct:
func processData(data: String?) -> String {
// Assuming getProcessedData(:) returns your processed data
guard let result = getProcessedData(data) else {
return "Not found"
}
return result
}
Also, your function is missing a return type. You must specify the return type like -> TYPE in all functions that return some value.
Those answer were written till mine are right. There is one way: with handler check get result and use by your point.
enum Errors: Error {
case noData
case unknownError
}
func progress(data: String?, completionHandler: #escaping (_ result: String? , _ error: Error?) -> Void ) {
guard let data = data else {
// Data is missing
throw nil, Errors.noData
}
// Do other things, and throw if necessary
result = data
return result, nil
}
// example of calling this function
process(data: "A data to process"){(result, error) -> Void in
//do any stuff
/*if error == nil {
}*/
}
A good practice in swift would be to use correctly the throws errors
This is an example inspired from yours :
enum Errors: Error {
case noData
case unknownError
}
func progress(data: String?) throws -> String {
guard let data = data else {
// Data is missing
throw Errors.noData
}
// Do other things, and throw if necessary
result = data
return result
}
do {
try process(data: "A data to process")
} catch {
print("An error occurred: \(error)")
}
You can try this code as is in a Swift Playgound
Your function needs to be explicit about returning something with e.g. -> String Also do-catch is for methods that can throw an error. It seems like you need to take a look at how to use optionals. Optionals can have a value or they can have no value.
fun processData(data: String) -> String {
var result: String?
// Do some processing and assign the result to result variable
guard let result = result else { return "Not Found" }
return result
}
I have a singleton that manages a set of data. If for some reason that data is unavailable I'd like the creation of the singleton to fail by throwing an exception. The compiler isn't a fan of marking the getter with throws NOTE: I realize I could handle this other ways but now I'm curious if it's even possible.
class Foo {
class var `default`: Foo {
let instance = Foo()
return instance
}
private init() throws {
// Throw if unable to load required data...
}
}
You can do it like this(code from my playground), downside every call of your singleton will have to be done with try.
enum FooError : Error {
case RuntimeError(String)
}
class Foo {
static func defaultFoo() throws -> Foo {
if let instance = Foo("Success") {
return instance
} else {
throw FooError.RuntimeError("Singleton is not created")
}
}
private init?(_ parameter: String?) {
if let _ = parameter {
print("Init Succeded")
} else {
print("Init failed")
return nil;
}
}
}
try Foo.defaultFoo()
I have the fallowing code:
struct AInt {
var aInt: Int
}
struct ADouble {
var aDouble: Double
static func convert(aInt: AInt) throws -> ADouble {
return ADouble(aDouble: Double(aInt.aInt))
}
}
struct B {
func doAction(aInts: [AInt]) throws -> [ADouble] {
return aInts.map { aInt in
do {
try ADouble.convert(aInt)
}
catch {
print(error)
}
}
// ^^^ error here: Missing return in a closure expected to return 'ADouble'
}
}
let aInts = [AInt(aInt: 2), AInt(aInt: 3)]
let b = B()
do {
print(try b.doAction(aInts))
}
catch {}
When i'm trying to convert [AInt] to [ADouble] in .map using function that can throw error, i get this error:
Missing return in a closure expected to return 'ADouble'.
Well, i decided to add return statement in the end of .map like this:
return aInts.map { aInt in
do {
try ADouble.convert(aInt)
}
catch {
print(error)
}
return ADouble(aDouble: 2.2)
}
Error disappear, but when i print try b.doAction(aInts) on same aInts array, i get this: [ADouble(aDouble: 2.2), ADouble(aDouble: 2.2)], i.e. it prints my ADouble(aDouble: 2.2) that i set manually. Obviously, it's not what i want, so then i try to add return before try ADouble.convert(aInt) like this:
return aInts.map { aInt in
do {
return try ADouble.convert(aInt)
}
catch {
print(error)
}
return ADouble(aDouble: 2.2)
}
And now i get right result: [ADouble(aDouble: 2.0), ADouble(aDouble: 3.0)]. But this code still doesn't work without return statement in the end of the .map. Any ideas how to get rid of it?
The map() method is declared as
#rethrows public func map<T>(#noescape transform: (Self.Generator.Element) throws -> T) rethrows -> [T]
which means (if I understand it correctly) that the transform can
either return a value or throw an error (and this will be
forwarded to the caller).
This compiles and works as expected:
struct B {
func doAction(aInts: [AInt]) throws -> [ADouble] {
return try aInts.map { aInt in
return try ADouble.convert(aInt)
}
}
}
An error thrown from ADouble.convert(aInt) will be forwarded to
the caller of map() and from there to the caller of doAction().