- Name:
effect-as-a-clause
- Proposed by: @mominul @satvikpendem
- Original proposal (optional): (https://github.com/rust-lang/keyword-generics-initiative/issues/14)
Design
We want to propose the usage of the effect
clause to achieve operation genericity, for example:
#![allow(unused)] fn main() { trait Read { fn read(&mut self, buf: &mut [u8]) -> Result<usize> effect async; fn read_to_string(&mut self, buf: &mut String) -> Result<usize> effect async { .. } } /// Function to read from the file into a string which may exhibit async or const effect fn read_to_string(path: &str) -> io::Result<String> effect async, const { let mut string = String::new(); // We can be conditional over the context the function has been called from, // only when the function declaration has the `effect` clause if async || !async { let mut file = File::open("foo.txt")?; // File implements Read // Because `read_to_string` is also an `effect` function that may or may not exhibit // async-ness par the declaration, we can use it on both contexts (async/sync) // we are placing the condition on. file.read_to_string(&mut string)?; // .await will be inferred. } else { // must be const // As the `read_to_string` doesn't exhibit const-ness, we'll need to handle it ourselves. string = include_str!(path).to_string(); } Ok(string) } /// A normal function fn read() { let data = read_to_string("hello.txt").unwrap(); } /// A async function async fn read() { let data = read_to_string("hello.txt").await.unwrap(); } /// A const function const fn read() { let data = read_to_string("hello.txt").unwrap(); } }
So in a nutshell, a function declaration with an effect
clause is a special type of function that may or may not exhibit async or const behavior(effect) and it depends on the context of the function being called from and we can execute a different piece of code according to the context from the function was called from too (like the const_eval_select
, resolves #6):
#![allow(unused)] fn main() { fn function() -> Result<()> effect async, const { // ... if async { // code for handling stuff asynchronously } else if const { // code for handling stuff `const`-way else { // code for handling stuff synchronously } // ... } }
base (reference)
#![allow(unused)] fn main() { /// A trimmed-down version of the `std::Iterator` trait. pub trait Iterator { type Item; fn next(&mut self) -> Option<Self::Item>; fn size_hint(&self) -> (usize, Option<usize>); } /// An adaptation of `Iterator::find` to a free-function pub fn find<I, T, P>(iter: &mut I, predicate: P) -> Option<T> where I: Iterator<Item = T> + Sized, P: FnMut(&T) -> bool; }
always async
#![allow(unused)] fn main() { pub trait Iterator { type Item; async fn next(&mut self) -> Option<Self::Item>; fn size_hint(&self) -> (usize, Option<usize>); } pub async fn find<I, T, P>(iter: &mut I, predicate: P) -> Option<T> where I: Iterator<Item = T> + Sized, P: async FnMut(&T) -> bool; }
maybe async
#![allow(unused)] fn main() { pub trait Iterator { type Item; fn next(&mut self) -> Option<Self::Item> effect async; fn size_hint(&self) -> (usize, Option<usize>); } pub fn find<I, T, P>(iter: &mut I, predicate: P) -> Option<T> where I: Iterator<Item = T> + Sized, P: FnMut(&T) -> bool effect async; effect async }
generic over all modifier keywords
#![allow(unused)] fn main() { pub trait Iterator { type Item; fn next(&mut self) -> Option<Self::Item> effect async, const; fn size_hint(&self) -> (usize, Option<usize>); } pub fn find<I, T, P>(iter: &mut I, predicate: P) -> Option<T> where I: Iterator<Item = T> + Sized, P: FnMut(&T) -> bool effect async, const; effect async, const }
Notes
We can introduce maybe
keyword instead of effect
if it seems more appropriate terminology for the semantics described in this proposal.