Guia 32 de 53
Handlers e escopos de recursos
Handlers léxicos, substituições tipadas e limpeza determinística de recursos.
Nesta página
Handlers léxicos, substituições tipadas e limpeza determinística de recursos.
Handlers antes do bloco¶
with handler_value { corpo } avalia e instala o handler antes do corpo. A semântica é a mesma de handle corpo with handler_value. Blocos aninhados restauram o handler externo, e retornos e limpeza mantêm suas fronteiras léxicas. O valor precisa ser um Handler comprovado.
Pratique na By Example · Programa fonte
// Prefix handler installation keeps the interpretation next to its scope.
// expected:
// memory
// inner
// memory
effect Config {
fn read() -> str
}
fn load() with Config -> str = perform Config::read()
let memory = handler {
Config::read() => "memory",
}
let inner = handler {
Config::read() => "inner",
}
print(with memory { load() })
let nested = with memory {
let answer = with inner { load() }
answer
}
print(nested)
print(with memory { load() })Escopos de recursos visíveis¶
using recurso = aquisição { corpo } instala a limpeza após uma aquisição bem-sucedida. O tipo precisa implementar core::cleanup::Cleanup. A limpeza acompanha saídas léxicas e transferências de ownership a desarmam. Um método apenas chamado close não comprova o protocolo.
Pratique na By Example · Programa fonte
// 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))Substituições tipadas de handlers¶
Um handler com seletores de operações substitui apenas essas operações e preserva a cobertura de efeitos e o tipo de resposta restantes. Assinaturas e corpos são checados contra o efeito real. A composição preserva o frame e a semântica léxica de abort.
Pratique na By Example · Programa fonte
// Composition preserves base evaluation, closures and lexical cleanup.
effect Clock { fn now() -> int; fn zone() -> str }
effect Store { fn put(value: int) -> int }
var calls = 0
var builds = 0
var events = ""
let fixed_time = 42
let __zolo_frame_id_0 = 0
let __zolo_handler_override_compose_0 = 0
let real_world: handler<Clock + Store, int> = handler {
Clock::now() => resume(1),
Clock::zone() => resume("UTC"),
Store::put(value) => resume(value + 1),
}
fn choose(value: handler<Clock + Store, int>) -> handler<Clock + Store, int> { value }
// Forwarding an opaque handler parameter does not establish composition origin.
let test_world = ({ builds += 1; real_world }) with {
Clock::now() => { calls += 1; defer { events += "defer/" }; fixed_time + __zolo_frame_id_0 + __zolo_handler_override_compose_0 },
}
print(with test_world { perform Clock::now() + perform Store::put(2) })
print(with test_world { perform Clock::zone(); 7 })
print(with test_world { perform Clock::now() })
print(calls)
print(events)
print(builds)
let twice = test_world with { Store::put(value: int) => value + 10 }
print(with twice { perform Store::put(2) })
fn lexical(value: handler<Clock + Store, int>) -> int {
defer { events += "caller/" }
with value { return 9 }
0
}
print(lexical(twice))
print(events)Vários recursos no mesmo escopo¶
Separe as aquisições com vírgulas no cabeçalho using. Elas executam da esquerda para a direita, e as posteriores podem usar os recursos anteriores. A liberação acontece na ordem inversa. Se uma aquisição falhar, todos os recursos já adquiridos serão liberados.
// 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: 3Continue aprendendo¶
DOCS / OPINIÃO
Esta página deixou alguma pergunta?
Conte onde a explicação perdeu você. A documentação também faz parte da experiência da linguagem.