Examples in V
struct User {
	id int
	name string
}

struct Repo {
	users []User
}

fn new_repo() Repo {
	return Repo {
		users: [User{1, 'Andrew'}, User {2, 'Bob'}, User {10, 'Charles'}]
	}
}

fn (r Repo) find_user_by_id(id int) ?User {
	for user in r.users {
		if user.id == id {
			// V automatically wraps this into an option type 
			return user
		}
	}
	return error('User $id not found')
}

fn main() {
	repo := new_repo()
	user := repo.find_user_by_id(10) or { // Option types must be handled by `or` blocks 
		return  // `or` block must end with `return`, `break`, or `continue` 
	}
	println(user.id) // "10" 
	println(user.name) // "Charles"
}

// V combines Option and Result into one type, so you don't need to decide which one to use.
// The amount of work required to "upgrade" a function to an optional function is minimal: you have to add a ? to the return type and return an error when something goes wrong.
// If you don't need to return an error, you can simply return none.
// This is the primary way of handling errors in V. They are still values, like in Go, but the advantage is that errors can't be unhandled, and handling them is a lot less verbose.
// err is defined inside an or block and is set to the string message passed to the error() function. err is empty if none was returned.
user := repo.find_user_by_id(7) or {
	println(err) // "User 7 not found"
	return
}

// You can also propagate errors:
resp := http.get(url)?
println(resp.body)

// http.get returns ?http.Response. It was called with ?, so the error is propagated to the calling function (which must return an optional) or in case of main leads to a panic.
// Basically the code above is a shorter version of
resp := http.get(url) or {
	panic(err)
}
println(resp.body)

// V does not have a way to force unwrap an optional (like Rust's unwrap() or Swift's !). You have to use or { panic(err) } instead.