Skip to content

Commit

Permalink
exit-once: Clarify that run() ins't prescriptive (#190)
Browse files Browse the repository at this point in the history
Clarify that the example of using `run()` and `log.Fatal` isn't intended
to be prescriptive.

Provide samples of using `os.Exit` directly, using different exit codes,
and clarify that there's flexibility here on how everything is set up.

Refs #189
  • Loading branch information
abhinav committed Jul 26, 2023
1 parent 301dcaa commit 7f06a53
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 0 deletions.
40 changes: 40 additions & 0 deletions src/exit-once.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,43 @@ func run() error {

</td></tr>
</tbody></table>

The example above uses `log.Fatal`, but the guidance also applies to
`os.Exit` or any library code that calls `os.Exit`.

```go
func main() {
if err := run(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
```

You may alter the signature of `run()` to fit your needs.
For example, if your program must exit with specific exit codes for failures,
`run()` may return the exit code instead of an error.
This allows unit tests to verify this behavior directly as well.

```go
func main() {
os.Exit(run(args))
}

func run() (exitCode int) {
// ...
}
```

More generally, note that the `run()` function used in these examples
is not intended to be prescriptive.
There's flexibility in the name, signature, and setup of the `run()` function.
Among other things, you may:

- accept unparsed command line arguments (e.g., `run(os.Args[1:])`)
- parse command line arguments in `main()` and pass them onto `run`
- use a custom error type to carry the exit code back to `main()`
- put business logic in a different layer of abstraction from `package main`

This guidance only requires that there's a single place in your `main()`
responsible for actually exiting the process.
40 changes: 40 additions & 0 deletions style.md
Original file line number Diff line number Diff line change
Expand Up @@ -1865,6 +1865,46 @@ func run() error {
</td></tr>
</tbody></table>

The example above uses `log.Fatal`, but the guidance also applies to
`os.Exit` or any library code that calls `os.Exit`.

```go
func main() {
if err := run(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
```

You may alter the signature of `run()` to fit your needs.
For example, if your program must exit with specific exit codes for failures,
`run()` may return the exit code instead of an error.
This allows unit tests to verify this behavior directly as well.

```go
func main() {
os.Exit(run(args))
}

func run() (exitCode int) {
// ...
}
```

More generally, note that the `run()` function used in these examples
is not intended to be prescriptive.
There's flexibility in the name, signature, and setup of the `run()` function.
Among other things, you may:

- accept unparsed command line arguments (e.g., `run(os.Args[1:])`)
- parse command line arguments in `main()` and pass them onto `run`
- use a custom error type to carry the exit code back to `main()`
- put business logic in a different layer of abstraction from `package main`

This guidance only requires that there's a single place in your `main()`
responsible for actually exiting the process.

### Use field tags in marshaled structs

Any struct field that is marshaled into JSON, YAML,
Expand Down

0 comments on commit 7f06a53

Please sign in to comment.