My question is not very specific to kotlin-native,...
# kotlin-native
v
My question is not very specific to kotlin-native, but since I'm writing a KN library I would like to get some feedback from the people here: In the bindings I'm writing I'm wrapping lots of C functions that can "throw" errors. I would like to avoid having multiple signatures for every "throwing" method so I need to choose which signature I'm going to generate: I can either return a
Result<T>
to make the the error explicit in the signature, or I can add a
@Throws
annotation and just throw the exception. Currently I'm favoring the
Result<T>
approach but it has some issues (like the Exception type not being generic). Any opinions on this?
h
I would not „fight“ against the language and would use @Throws
v
The thing I'm worried about with using
@Throws
is that a lot of these methods can frequently "throw" and aside from the annotation on the signature there is no other indication that it can. AFAIK Intellij does not warn when calling a
@Throws
method that does not handle the exception. Given the tendency for many developers to not read the docs I'm afraid many of these will be missed leading to frequent runtime crashes (not ideal in a GUI library). I put "throw" between quotes because the methods I'm talking about are generated bindings from C code, which use an error pointer argument that gets populated when the C function returned early with an error.
Untitled.kt
This is an example of the current state of the bindings for some IO-related methods. The current implementation uses
Result<T>
. I put
getOrThrow()
in there for demonstration purposes. If the
openReadWrite
uses
@Throws
, then it is easy to forget to handle the case where the file does not exist, the file is a directory, or the operation was cancelled (if a cancellable was provided and later used asynchronously)
Untitled.kt
This is an example of how the error can be handled with
Result<T>
. The benefit is that the user will be forced to do something with the failure case. My main issue with this solution is that
ex
from
Result<T>
is a
Throwable
while I already know it cannot be anything else than
GlibException
or one of its subclasses.
Maybe the cleanest way is to have my own Result-like type for the bindings, or provide some additional extensions on
Result<T>
which narrows down the exception type, but it feels dirty.
h
I absolutely understand your use case. Kotlin does not have checked exceptions by design and wrapping everything in a
Result<T>
isn't a common practice. The mapped Java File APIs are similar:
Copy code
File("foo.txt").writeText("foo")
This could also fail with IOException, but there is no hint/language design to explicitly catch the exception. But Kotlin's design is also pragmatic. If it does work for you, use your API design, in this case, I would use your own
Result
class to provide more information.
v
Thanks for your input, I'm still going back and forth on both approaches.
s
This is probably a good question for #library-development. I have been in this situation and what I/we came up with was that there was no single way to handle errors across all functions in a library. Maybe your library is very small or specialized, but a single approach for all functions probably won't be appropriate for all use cases or functions. Some combination of the two approaches made sense for us for how different functions are used. We also incorporated a third category for being lenient with exceptions, the
...OrNull
variety of functions which catch all exceptions and return null.
v
Yes, I agree, not all errors should get the same treatment. However we are currently generating Kotlin code for a large amount of libraries driven by gobject introspection GIR files so there is a large amount of error-throwing methods to cover with varying use cases and for most of them we don't know the exact semantics, only that they "throw". Currently it generates 350+ error-throwing methods and there is a significant number of methods that are skipped for other reasons (like argument or return values for which we don't have conversions yet) Hence, we need to find a general solution that works for most cases, with the possibility of manually providing alternative error handling implementations for specific methods/functions but that is a manual process. The reason the
Result<T>
approach looks interesting (which is the current implementation) is that it provides some flexibility to the library user. The
Result<T>
class provides some utilities like
getOrNull()
,
getOrThrows()
.
And since we're covering a large amount of libraries that all use the same GLib/GError error mechanism, I'm starting to warm up to the idea of having a specialized
Result
type that encapsulates the GError details more clearly using the type system.