Syntax and naming
This page describes various keywords and surface-level syntactical approaches that can be used to implement the feature. More may be added as new ideas arise.
Defining context items
Keyword
The original blog post about this feature called context items a capability
:
#![allow(unused)] fn main() { capability basic_arena<'a> = &'a BasicArena; }
Some people have pushed back against this. While these items are related to
capabilities, that isn't the full story. A more neutral name is context
.
#![allow(unused)] fn main() { context basic_arena<'a> = &'a BasicArena; }
Types and bounds
Context objects are simply items that may have where clauses attached to them. This means it is possible to define a "bare" context with no type:
#![allow(unused)] fn main() { context anything_goes; }
Or a specific type, or a generic context with bounds:
#![allow(unused)] fn main() { context basic_arena = BasicArena; context any_allocator: Allocator; context debug_iter: Iterator where Self::Item: Sized; }
It's unclear what the best way is to write these. People might be confused by
the use of =
to name an exact type. One approach is to treat them just like
function arguments.
#![allow(unused)] fn main() { context basic_arena: BasicArena; context any_allocator: impl Allocator; }
But then we also want to be able to write where
clauses on the type, which you
can do with argument-position impl Trait. How would we write debug_iter
?
#![allow(unused)] fn main() { // This could work... context debug_iter: impl Iterator where debug_iter::Item: Sized; // ...or we could allow you to write this. context debug_iter: impl Iterator where Self::Item: Sized; }
The problem with using Self
is that it looks more like could we would write in
a trait definition, where :
is used to denote supertraits. It's not clear
whether this would be confusing or not, just something to consider.
Whatever we choose, we would probably want given
clauses to act the same way.
Note that in where clauses we treat the name of the context as a type (as in the first example above), while in expression position we treat it as a value.
Providing and demanding context
The original blog post about this feature introduced the with
keyword for
both introducing contexts in an expression and requiring them as clauses.
fn deserialize<'a>(bytes: &[u8]) -> Result<&'a Foo, Error> with arena::basic_arena, { arena::basic_arena.alloc(Foo::from_bytes(bytes)?) } fn main() -> Result<(), Error> { with arena::basic_arena = &arena::BasicArena::new() { let foo: &Foo = deserialize(read_bytes()?)?; println!("foo: {:?}", foo); } Ok(()) }
However, it was pointed out that with
can mean many things, and more specific
keywords could be used like given
:
fn deserialize<'a>(bytes: &[u8]) -> Result<&'a Foo, Error> given arena::basic_arena, { arena::basic_arena.alloc(Foo::from_bytes(bytes)?) } fn main() -> Result<(), Error> { given arena::basic_arena = &arena::BasicArena::new() { let foo: &Foo = deserialize(read_bytes()?)?; println!("foo: {:?}", foo); } Ok(()) }
This can still be used in both places. It's arguably much more clear than with
clauses that a given
clause requires context to be provided to it.