# What to do about \`static func main() throws\`?

**URL:** <https://forums.swift.org/t/what-to-do-about-static-func-main-throws/69031>\
**Category:** Discussion\
**Created:** [December 16, 2023, 10:52pm UTC](https://forums.swift.org/t/what-to-do-about-static-func-main-throws/69031 "2023-12-16T22:52:18Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![taylorswift](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/taylorswift/32/43413_2.png) [@taylorswift](https://forums.swift.org/u/taylorswift)\
**Post date:** [December 16, 2023, 10:52pm UTC](https://forums.swift.org/t/what-to-do-about-static-func-main-throws/69031/1 "2023-12-16T22:52:18Z")

</div>

ever since backtracing landed in 5.9, `@main` with a throwing `main` function is [quite useless](https://forums.swift.org/t/how-to-opt-out-of-swift-5-9-interactive-exit-behavior/67599/17). (to me, anyway.)

to adapt, i’ve started making use of some function decorators such as:

```swift
#if canImport(Glibc)
import Glibc
#elseif canImport(Darwin)
import Darwin
#else
#error("unsupported platform")
#endif

extension SystemProcess
{
    @MainActor
    public static
    func `do`(_ operation:() async throws -> Void) async
    {
        do
        {
            try await operation()
        }
        catch let error
        {
            print(error)
            exit(1)
        }
    }
}

```

```swift
@main
enum Main
{
    static
    func main() async
    {
        await SystemProcess.do(Self._main)
    }

    private static
    func _main() async throws
    {

```

that’s not the worst thing in the world, but it seems odd to me that `static func main() async throws` is still allowed when there are likely no situations in which you would ever want to throw from `main`. the backtrace will never contain any useful information, it just wastes 30 seconds of CPU time.

```plaintext
💣 Program crashed: Illegal instruction at 0x00007f95c110cc82

Thread 5 "NIO-ELT-0-#0" crashed:

0 0x00007f95c110cc82 _assertionFailure(_:_:file:line:flags:) + 354 in libswiftCore.so
1 0x00007f95c1173c6c swift_errorInMain + 619 in libswiftCore.so
2 0x00007f95c1822fb0 completeTaskAndRelease(swift::AsyncContext*, swift::SwiftError*) in libswift_Concurrency.so

Backtrace took 28.80s

```

what should we do about `static func main() async throws`?

---

<div class="post-metadata">

**Author:** ![mklbtz](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/mklbtz/32/21972_2.png) [@mklbtz](https://forums.swift.org/u/mklbtz)\
**Post date:** [December 17, 2023, 6:31pm UTC](https://forums.swift.org/t/what-to-do-about-static-func-main-throws/69031/2 "2023-12-17T18:31:57Z")

</div>

It feels to me that — instead of “crashing” — throwing from main should dump the error (optionally?) and then exit(1). That seems like a reasonable default behavior.

It could be interesting to see some kind of standardized SystemError type that when thrown would have different well defined behaviors. Kinda like LocalizableError. But maybe that’s always too application specific to standardize.

---

<div class="post-metadata">

**Author:** ![ksluder](https://avatars.discourse-cdn.com/v4/letter/k/eada6e/32.png) [@ksluder](https://forums.swift.org/u/ksluder)\
**Post date:** [December 17, 2023, 7:40pm UTC](https://forums.swift.org/t/what-to-do-about-static-func-main-throws/69031/3 "2023-12-17T19:40:50Z")

</div>

> [@mklbtz](#):
>
> It feels to me that — instead of “crashing” — throwing from main should dump the error (optionally?) and then exit(1). That seems like a reasonable default behavior.

That would lead to a situation where these three forms don’t have the same behavior:

```swift
func main() throws {
  try someThrowingFn()
}

func main() {
  try! someThrowingFn()
}

// main.swift
try! someThrowingFn()

```

To not get a backtrace from the first would be frustrating, because the fix would be to turn all your `try`s into `try!`s.

If you don’t want `main` to produce a backtrace on failure, you can wrap everything in a `do { } catch { }` that dumps the error and exits. Then you’re visibly and explicitly opting out of the backtrace capture.

---

<div class="post-metadata">

**Author:** ![taylorswift](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/taylorswift/32/43413_2.png) [@taylorswift](https://forums.swift.org/u/taylorswift)\
**Post date:** [December 17, 2023, 8:02pm UTC](https://forums.swift.org/t/what-to-do-about-static-func-main-throws/69031/4 "2023-12-17T20:02:23Z")

</div>

> [@ksluder](#):
>
> To not get a backtrace from the first would be frustrating, because the fix would be to turn all your `try`s into `try!`s.

why would this be preferable to logging the error and exiting gracefully? the backtrace would never contain any useful information, it will just cause your terminal to hang for 30 seconds.

---

<div class="post-metadata">

**Author:** ![ksluder](https://avatars.discourse-cdn.com/v4/letter/k/eada6e/32.png) [@ksluder](https://forums.swift.org/u/ksluder)\
**Post date:** [December 17, 2023, 8:03pm UTC](https://forums.swift.org/t/what-to-do-about-static-func-main-throws/69031/5 "2023-12-17T20:03:17Z")

</div>

Why wouldn’t the backtrace contain useful information? You can have more than one `try` in a function. Wouldn’t you like to know which one threw?

---

<div class="post-metadata">

**Author:** ![taylorswift](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/taylorswift/32/43413_2.png) [@taylorswift](https://forums.swift.org/u/taylorswift)\
**Post date:** [December 17, 2023, 10:15pm UTC](https://forums.swift.org/t/what-to-do-about-static-func-main-throws/69031/6 "2023-12-17T22:15:43Z")

</div>

it would not trace where the error was thrown from, it would only trace where the error was caught, which is always the compiler-generated `swift_errorInMain`.

---

<div class="post-metadata">

**Author:** ![ksluder](https://avatars.discourse-cdn.com/v4/letter/k/eada6e/32.png) [@ksluder](https://forums.swift.org/u/ksluder)\
**Post date:** [December 18, 2023, 1:41am UTC](https://forums.swift.org/t/what-to-do-about-static-func-main-throws/69031/7 "2023-12-18T01:41:54Z")

</div>

Oof, that’s kind of a bummer.

---

<div class="post-metadata">

**Author:** ![Max\_Desiatov](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/max_desiatov/32/20654_2.png) [@Max\_Desiatov](https://forums.swift.org/u/Max_Desiatov)\
**Post date:** [December 18, 2023, 3:37pm UTC](https://forums.swift.org/t/what-to-do-about-static-func-main-throws/69031/8 "2023-12-18T15:37:50Z")

</div>

> [@taylorswift](#):
>
> it would not trace where the error was thrown from, it would only trace where the error was caught, which is always the compiler-generated `swift_errorInMain`.

@al45tair maybe we could consider changing the default behaviour to trace back errors thrown from `main` if that's possible at all?

---

<div class="post-metadata">

**Author:** ![taylorswift](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/taylorswift/32/43413_2.png) [@taylorswift](https://forums.swift.org/u/taylorswift)\
**Post date:** [December 18, 2023, 9:16pm UTC](https://forums.swift.org/t/what-to-do-about-static-func-main-throws/69031/9 "2023-12-18T21:16:20Z")

</div>

for context, there was [some discussion](https://forums.swift.org/t/exit-codes-from-main/61698) about this last year, but i don’t recall it leading anywhere.

---

<div class="post-metadata">

**Author:** ![etcwilde](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/etcwilde/32/13687_2.png) [@etcwilde](https://forums.swift.org/u/etcwilde)\
**Post date:** [December 19, 2023, 2:51am UTC](https://forums.swift.org/t/what-to-do-about-static-func-main-throws/69031/10 "2023-12-19T02:51:15Z")

</div>

Yeah, it didn't end up going anywhere. I had it pretty well working, but didn't have the time or energy to argue why we don't actually want to expose `exit` directly. Given the discovery that `CommandLine.arguments` doesn't work consistently on Linux, I may take another stab at proposing a "full-featured" main-entrypoint that passes in the arguments and has a return-code, though that would likely not be for throwing purposes, but more so that I can move away from the ol'

```swift
@_cdecl("main")
func main(_ argc: Int32, _ argv: UnsafePointer<UnsafePointer<Int8>>) -> Int {
    // ...
    return 0
}

```

That said, Doug's suggestion of having the ability to return directly from top-level code is growing on me with every script I write, especially in `guard` blocks.

```swift
guard let thingamabob = getAThingamabob() else {
  print("No thingamabob for you")
  return 1
}

```

would make me happier than the current `fatalError` that you get to do today. I don't really need a stacktrace for `main` there.

I can't guarantee I have time or energy to push this through at the moment either though. ¯\_(ツ)\_/¯

---

<div class="post-metadata">

**Author:** ![bbrk24](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/bbrk24/32/27370_2.png) [@bbrk24](https://forums.swift.org/u/bbrk24)\
**Post date:** [December 19, 2023, 3:49am UTC](https://forums.swift.org/t/what-to-do-about-static-func-main-throws/69031/11 "2023-12-19T03:49:40Z")

</div>

> [@etcwilde](#):
>
> ```swift
> @_cdecl("main")
> func main(_ argc: Int32, _ argv: UnsafePointer<UnsafePointer<Int8>>) -> Int
> 
> ```

Shouldn’t this also return `Int32`, not `Int`? The calling conventions are compatible (at least on x86-64), but this just goes to show how fragile it is.

From what I remember, C# allows its `Main` method to return `void` or `int`, and allows it to take either no arguments or a `string[]` argument. (C# doesn’t mark whether a method throws in its type system, so I have no advice in that regard.)

---

<div class="post-metadata">

**Author:** ![ksluder](https://avatars.discourse-cdn.com/v4/letter/k/eada6e/32.png) [@ksluder](https://forums.swift.org/u/ksluder)\
**Post date:** [December 19, 2023, 4:05am UTC](https://forums.swift.org/t/what-to-do-about-static-func-main-throws/69031/12 "2023-12-19T04:05:46Z")

</div>

> [@bbrk24](#):
>
> Shouldn’t this also return `Int32`, not `Int`?

The spec says `main` has return type `int`, but different operating systems on the same hardware can choose between LP, ILP and LLP. So I think the real answer is “whatever works”.

---

<div class="post-metadata">

**Author:** ![etcwilde](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/etcwilde/32/13687_2.png) [@etcwilde](https://forums.swift.org/u/etcwilde)\
**Post date:** [December 19, 2023, 4:32am UTC](https://forums.swift.org/t/what-to-do-about-static-func-main-throws/69031/13 "2023-12-19T04:32:31Z")

</div>

> [@bbrk24](#):
>
> > [@etcwilde](#):
> >
> > ```swift
> > func main(_ argc: Int32, _ argv: UnsafePointer<UnsafePointer<Int8>>) -> Int
> > 
> > ```

Maybe `CInt`? ¯\_(ツ)\_/¯ Anyway, I think letting the compiler do the right thing consistently is probably a good thing to do. Given that `exit` skips `deinit`'s and `defer` blocks, and is generally not something we want to encourage in libraries and frameworks, or really anywhere that isn't the main function, I still think that just returning from `main` is the right thing. But my focus is needed elsewhere at the moment unfortunately.

---

<div class="post-metadata">

**Author:** ![al45tair](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/al45tair/32/17985_2.png) [@al45tair](https://forums.swift.org/u/al45tair)\
**Post date:** [December 19, 2023, 4:09pm UTC](https://forums.swift.org/t/what-to-do-about-static-func-main-throws/69031/14 "2023-12-19T16:09:42Z")

</div>

> [@Max\_Desiatov](#):
>
> @al45tair maybe we could consider changing the default behaviour to trace back errors thrown from `main` if that's possible at all?

I'm not quite sure what you mean by this. Swift error handling isn't done by stack unwinding, so we don't actually know where the error was originally raised.

I'm honestly not certain what the correct behaviour here is; clearly if you `throw` an error from main, there's nothing to catch it and today that means you've crashed. There's no way in an error to specify an exit code, so it can't really be any kind of "clean" exit right now. On the other hand, using `exit()` from the C library to terminate the program is awkward in other ways (as mentioned in [the aforementioned discussion from last year](https://forums.swift.org/t/exit-codes-from-main/61698), which I'm reading as I write this).

It seems to me that this is more of a language design issue rather than a runtime issue per se, and that there probably _should_ be a more Swift-y way to exit with an error code.

---

<div class="post-metadata">

**Author:** ![ksluder](https://avatars.discourse-cdn.com/v4/letter/k/eada6e/32.png) [@ksluder](https://forums.swift.org/u/ksluder)\
**Post date:** [December 19, 2023, 4:16pm UTC](https://forums.swift.org/t/what-to-do-about-static-func-main-throws/69031/15 "2023-12-19T16:16:01Z")

</div>

> [@al45tair](#):
>
> I'm not quite sure what you mean by this. Swift error handling isn't done by stack unwinding, so we don't actually know where the error was originally raised.

If you use `try!`, the backtrace is taken from the line that failed. Whereas if you use `do { try }`, every single failure’s backtrace starts from the same spot: the synthesized `catch` that wraps `main`.

---

<div class="post-metadata">

**Author:** ![al45tair](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/al45tair/32/17985_2.png) [@al45tair](https://forums.swift.org/u/al45tair)\
**Post date:** [December 19, 2023, 4:21pm UTC](https://forums.swift.org/t/what-to-do-about-static-func-main-throws/69031/16 "2023-12-19T16:21:43Z")

</div>

> [@ksluder](#):
>
> If you use `try!`, the backtrace is taken from the line that failed. Whereas if you use `do { try }`, every single failure’s backtrace starts from the same spot: the synthesized `catch` that wraps `main`.

I'm not sure how that affects things. The point is that we'll get a backtrace from the place the program crashes, which is presently the point at which Swift spots that there's an error that it wasn't expecting. I _thought_ Max might be thinking that we could somehow find out where the `Error` originated, but I don't believe that's possible, since we've actually returned from the function(s) that threw it at that point.

---

<div class="post-metadata">

**Author:** ![benlings](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/benlings/32/13011_2.png) [@benlings](https://forums.swift.org/u/benlings)\
**Post date:** [December 19, 2023, 5:15pm UTC](https://forums.swift.org/t/what-to-do-about-static-func-main-throws/69031/17 "2023-12-19T17:15:53Z")

</div>

> [@al45tair](#):
>
> There's no way in an error to specify an exit code, so it can't really be any kind of "clean" exit right now

Would something like this work?

```swift
extension CommandLine {
  protocol Error: Swift.Error {
    var exitCode: CInt
  }
}

```

If an error that confoms to `CommandLine.Error` is thrown at top level, it exits the program with its `exitCode`.

---

<div class="post-metadata">

**Author:** ![ksluder](https://avatars.discourse-cdn.com/v4/letter/k/eada6e/32.png) [@ksluder](https://forums.swift.org/u/ksluder)\
**Post date:** [December 19, 2023, 5:35pm UTC](https://forums.swift.org/t/what-to-do-about-static-func-main-throws/69031/18 "2023-12-19T17:35:47Z")

</div>

> [@al45tair](#):
>
> The point is that we'll get a backtrace from the place the program crashes, which is presently the point at which Swift spots that there's an error that it wasn't expecting.

That is indeed the problem. If I use `try!`, the backtracer points at the actual `try!` line that threw:

```swift
crasher/main.swift:8: Fatal error: 'try!' expression unexpectedly raised an error: crasher.Err()

💣 Program crashed: System trap at 0x00000001920151e8

Thread 0 crashed:

0 0x00000001920151e8 _assertionFailure(_:_:file:line:flags:) + 268 in libswiftCore.dylib
1 0x0000000192081888 swift_unexpectedError + 516 in libswiftCore.dylib
2 static Main.main() + 144 in crasher at /Users/kyle/crasher/Sources/main.swift:8:8

     6│ @main struct Main {
     7│ public static func main() {
     8│ try! { throw Err() }()                                                                                                    
      │ ▲
     9│ try! { throw Err() }()
    10│ }

```

Whereas if I use `func main() throws`, the backtracer points ambiguously at the `struct main` declaration:

```swift
Swift/ErrorType.swift:200: Fatal error: Error raised at top level: crasher.Err()

💣 Program crashed: System trap at 0x00000001920151e8

Thread 0 crashed:

0 0x00000001920151e8 _assertionFailure(_:_:file:line:flags:) + 268 in libswiftCore.dylib
1 0x00000001920b2a24 swift_errorInMain + 492 in libswiftCore.dylib
2 crasher_main + 80 in crasher at /Users/kyle/crasher/Sources/main.swift:6:14

     4│ struct Err: Error { }
     5│ 
     6│ @main struct Main {                                                                                                                       
      │ ▲
     7│ public static func main() throws {
     8│ throw Err()

```

Can the second case be made equivalent to the first?

---

<div class="post-metadata">

**Author:** ![Joe\_Groff](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/joe_groff/32/1929_2.png) [@Joe\_Groff](https://forums.swift.org/u/Joe_Groff)\
**Post date:** [December 19, 2023, 5:40pm UTC](https://forums.swift.org/t/what-to-do-about-static-func-main-throws/69031/19 "2023-12-19T17:40:57Z")

</div>

> [@ksluder](#):
>
> Can the second case be made equivalent to the first?

Each `try!` introduces a trap in the caller for each throwing call in the subexpression. You still don't get the backtrace from where the error originated, only where it was caught and a fatal error triggered. Without special casing the code generation for a `@main` function, we can't make propagating an error out of it do the same thing, since the error was passed out of `main` into the pre-main function. In general, if you want backtrace information in an error from where it originated, then it'd have to be captured when the error was constructed in its initializer.

---

<div class="post-metadata">

**Author:** ![ksluder](https://avatars.discourse-cdn.com/v4/letter/k/eada6e/32.png) [@ksluder](https://forums.swift.org/u/ksluder)\
**Post date:** [December 19, 2023, 5:45pm UTC](https://forums.swift.org/t/what-to-do-about-static-func-main-throws/69031/20 "2023-12-19T17:45:13Z")

</div>

> [@Joe\_Groff](#):
>
> In general, if you want backtrace information in an error from where it originated, then it'd have to be captured when the error was constructed in its initializer.

I get why the backtrace can’t start where the error was thrown, but it’s pretty unfortunate that it can’t start at the line that caused control flow to exit the program. I would be in favor of special-casing `@main`, perhaps by collaborating with the pre-main function to preserve the IP of the line that branched out of `main`.

[Next page](https://forums.swift.org/t/what-to-do-about-static-func-main-throws/69031.md?page=2)
