Skip to content

08 · Error Handling

Visible resource scopes

using resource = acquisition { body } installs cleanup after a successful acquisition. The type must implement core::cleanup::Cleanup. Cleanup follows lexical exits and owned transfers disarm it. A method merely named close does not establish the protocol.

On this page

using resource = acquisition { body } installs cleanup after a successful acquisition. The type must implement core::cleanup::Cleanup. Cleanup follows lexical exits and owned transfers disarm it. A method merely named close does not establish the protocol.

Complete example

Visible resource scopes.

resource-scopes.zolo
Playground
// Resource scopes install cleanup after successful acquisition.

var acquired = 0
struct Resource { id: int }
fn acquire(id: int) -> Result<Resource, str> {
    acquired += 1
    if id < 0 { .Err("acquisition") } else { .Ok(Resource { id }) }
}
impl core::cleanup::Cleanup for Resource {
    fn close(self) { print("close"); print(self.id) }
}
fn read(id: int) -> Result<int, str> {
    let value = using file = acquire(id)? {
        defer print("body-defer")
        file.id + 10
    }
    Result::Ok(value)
}
print(read(1).unwrap())
print(read(-1).is_err())
print(acquired)
fn early() -> int {
    using file = Resource { id: 2 } { return 22 }
    0
}
print(early())
fn fail() -> Result<int, str> { .Err("body") }
fn propagated() -> Result<int, str> {
    let value = using file = Resource { id: 3 } { fail()? }
    Result::Ok(value)
}
print(propagated().is_err())
var count = 0
:outer loop {
    count += 1
    using file = Resource { id: count + 3 } {
        defer print("loop-defer")
        if count == 1 { continue :outer }
        break :outer
    }
}
let tail = using outer = Resource { id: 6 } {
    let inner = using inner = Resource { id: 7 } { inner.id }
    inner + outer.id
}
print(tail)
@linear struct Owned { id: int }
impl core::cleanup::Cleanup for Owned {
    fn close(self) { print("owned-close"); print(self.id) }
}
impl Owned {
    fn finish(sink self) { self.close(); let Owned { .. } = self }
}
using owned = Owned { id: 8 } { 0 }
using owned = Owned { id: 9 } {
    var __zolo_live_0 = true
    owned.finish()
    assert(__zolo_live_0)
    0
}
fn consume(sink owned: Owned) { owned.close(); let Owned { .. } = owned }
fn seven() -> int { 7 }
fn consume_with(sink owned: Owned, number: int) { assert(number == 7); consume(owned) }
using owned = Owned { id: 10 } {
    let __zolo_arg_0 = 13
    consume_with(owned, seven())
    assert(__zolo_arg_0 == 13)
    0
}

let transferred = using owned = Owned { id: 11 } { owned }
consume(transferred)

// The implicit cleanup owns the original binding even when the body shadows it.

let shadow = using file = Resource { id: 12 } {
    let file = Resource { id: 13 }
    file.id
}
print(shadow)
// Generic sink transfer hands the resource to the caller and disarms cleanup.

fn moved<T: core::cleanup::Cleanup>(sink value: T) -> T {
    using resource = value { resource }
}
consume(moved(Owned { id: 14 }))
macro scoped(value) { using resource = $value { consume(resource); 7 } }
print(scoped!(Owned { id: 15 }))
print(scoped!(Owned { id: 16 }))

// An owned generic factory temporary keeps its cleanup obligation.

fn acquire_owned() -> Owned { Owned { id: 17 } }
fn scoped_factory<T: core::cleanup::Cleanup>(factory: fn() -> T) -> int {
    let acquired = factory()
    using resource = acquired { 7 }
}
print(scoped_factory(acquire_owned))

Rules and behavior

Handlers and resource scopes · Documentation

From a checkout of the repository:

zolo examples/features/resource-scopes.zolo

Acquiring several resources

A comma-separated header acquires resources in order and installs cleanup after each successful acquisition. Cleanup runs in reverse order, including when a later acquisition fails.

04-multiple-resources.zolo
Playground
// Each successful acquisition installs cleanup before the next one starts.

struct File { name: str }
fn open(name: str) -> File { File { name } }
impl core::cleanup::Cleanup for File {
    fn close(self) { print("close {self.name}") }
}
let copied = using source = open("source"), target = open("target") {
    print("{source.name} -> {target.name}")
    3
}
// expect: source -> target

// expect: close target

// expect: close source

print(copied) // expect: 3

Global index

Find your way through Zolo

Try an idea

Start here

9 results

9 results

enespt-br