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"

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

// 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 {

// 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.