I'm new to rust's async programming and encountered something weird.
I want to introduce async in traits without boxing overhead.
I'm using nightly with the #![feature(type_alias_impl_trait)] feature.
Here's minimized code:
#![feature(type_alias_impl_trait)]
use std::path::Path;
use std::future::Future;
type Error = Box<dyn std::error::Error>;
#[tokio::main]
async fn main() -> Result<(), Error> {
tokio::spawn(async move {
"test".to_owned().super_async("x").await
});
Ok(())
}
trait AsyncTrait {
type Fut<'a, P>: Future<Output=()>
where
Self: 'a,
P: AsRef<Path> + 'a;
fn test_async<P: AsRef<Path>>(&self, p: P) -> Self::Fut<'_, P>;
}
impl AsyncTrait for String {
type Fut<'a, P> = impl Future<Output = ()> + 'a
where
Self: 'a,
P: AsRef<Path> + 'a;
fn test_async<P: AsRef<Path>>(&self, p: P) -> Self::Fut<'_, P> {
async move {
let bs = p.as_ref();
()
}
}
}
trait SuperAsync: AsyncTrait {
type SuperFut<'a, P>: Future<Output=()>
where
P: AsRef<Path> + 'a,
Self: 'a;
fn super_async<P: AsRef<Path>>(&self, p: P) -> Self::SuperFut<'_, P>;
}
impl<T: AsyncTrait> SuperAsync for T {
type SuperFut<'a, P> = impl Future<Output = ()> + 'a
where
P: AsRef<Path> + 'a,
Self: 'a;
fn super_async<P: AsRef<Path>>(&self, p: P) -> Self::SuperFut<'_, P> {
async move {
self.test_async(p).await;
()
}
}
}
Then I got the error message:
error: implementation of `AsRef` is not general enough
--> src/main.rs:45:5
|
45 | / tokio::spawn(async move {
46 | | "test".to_owned().super_async("x").await
47 | | });
| |______^ implementation of `AsRef` is not general enough
|
= note: `AsRef<Path>` would have to be implemented for the type `&'0 str`, for any lifetime `'0`...
= note: ...but `AsRef<Path>` is actually implemented for the type `&'1 str`, for some specific lifetime `'1`
I don't understand the error message. If I remove tokio::spawn or impl SuperAsync only for String, then the error disappers. Any help?
I was able to implement this:
#![feature(type_alias_impl_trait)]
use std::path::Path;
use std::future::Future;
type Error = Box<dyn std::error::Error>;
#[tokio::main]
async fn main() -> Result<(), Error> {
tokio::spawn(async move {
"test".to_owned().super_async("x").await
});
Ok(())
}
trait AsyncTrait {
type Fut<'a, P>: Future<Output=()>
where
Self: 'a,
P: AsRef<Path> + 'a + ?Sized;
fn test_async<'a, P: AsRef<Path> + ?Sized>(&self, p: &'a P) -> Self::Fut<'a, P>;
}
impl AsyncTrait for String {
type Fut<'a, P> = impl Future<Output = ()> + 'a
where
Self: 'a,
P: AsRef<Path> + 'a + ?Sized;
fn test_async<'a, P: AsRef<Path> + ?Sized>(&self, p: &'a P) -> Self::Fut<'a, P> {
async move {
let bs = p.as_ref();
()
}
}
}
trait SuperAsync: AsyncTrait {
type SuperFut<'a, P>: Future<Output=()>
where
P: AsRef<Path> + 'a + ?Sized,
Self: 'a;
fn super_async<'a, P: AsRef<Path> + ?Sized>(&'a self, p: &'a P) -> Self::SuperFut<'a, P>;
}
impl<T: AsyncTrait> SuperAsync for T {
type SuperFut<'a, P> = impl Future<Output = ()> + 'a
where
P: AsRef<Path> + 'a + ?Sized,
Self: 'a;
fn super_async<'a, P: AsRef<Path> + ?Sized>(&'a self, p: &'a P) -> Self::SuperFut<'a, P> {
async move {
self.test_async(p).await;
()
}
}
}
I changed the definition a little bit, now lifetime 'a is associated to self and P, also changing P into a reference, so we can also associate his and the AsRef lifetimes to to 'a.
By changing P into a reference, we enforce that we are not consuming it, and the data that this reference points to will live longer then the future returned. Maybe there is a way to receive a owned P value and ensure it's lifetime, but I couldn't figure that out.
The ?Sized was added because P solves into the str type (not &str) that is unsized.
Related
I am using crate clap v4。When I try to write something validating arguments against regex, I had some problem with lifetimes.
Document of ValueParser for convenience
My code as following:
pub fn validator_regex(r: &'static str) -> ValueParser {
ValueParser::from(move |s: &str| -> std::result::Result<&str, Error> {
let reg = regex::Regex::new(r).unwrap();
match reg.is_match(s) {
true => Ok(s),
false => Err(Error::from(format!("not matches {}", r))),
}
})
}
pub fn validator_identifier() -> ValueParser {
validator_regex("^[-_0-9a-zA-Z]+$")
}
And compilation error:
error: lifetime may not live long enough
--> main\./src\cmd\cmd_util.rs:440:21
|
437 | ValueParser::from(move |s: &str| -> std::result::Result<&str, Error> {
| - - let's call the lifetime of this reference `'2`
| |
| let's call the lifetime of this reference `'1`
...
440 | true => Ok(s),
| ^^^^^ returning this value requires that `'1` must outlive `'2`
Could any help me on these two questions:
how to validate regex in clap v4
why there' s lifetime may not live long enough error in this code, since the returned &str lives as long as the closure argument, and this can be compiled normally
pub fn validator_regex(r: &'static str) -> impl Fn(&str) -> Result<&str, String> {
move |s: &str| -> Result<&str, String> {
let reg = regex::Regex::new(r).unwrap();
match reg.is_match(s) {
true => Ok(s),
false => Err(format!("not match {}", r)),
}
}
}
Let's take a look at the From impl you're invoking:
impl<P> From<P> for ValueParser
where
P: TypedValueParser + Send + Sync + 'static,
Ok, take a look at TypedValueParser:
impl<F, T, E> TypedValueParser for F
where
F: Fn(&str) -> Result<T, E> + Clone + Send + Sync + 'static,
E: Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
T: Send + Sync + Clone,
So, the signature is not Fn(&str) -> Result<&str, E>, it is <T> Fn(&str) -> Result<T, E>. Is it different? Can't we just put &str in the place of T?
It is very different. Let's annotate the lifetimes:
// Fn(&str) -> Result<&str, E>
for<'a> Fn(&'a str) -> Result<&'a str, E>
// <T> Fn(&str) -> Result<T, E>
<&'b str> for<'a> Fn(&'a str) -> Result<&'b str, E>
Where does 'b come from? It can't be HRTB, as it is declared in the impl header (T) and not the trait bound (Fn). Therefore, it must be a fixed lifetime. See the mismatch? The lifetimes I called 'a and 'b are what the compiler calls '1 and '2, respectively. If 'b is fixed, it cannot be derived from the dynamic HRTB 'a.
The fix is simple: just don't return &str, return String instead:
pub fn validator_regex(r: &'static str) -> ValueParser {
ValueParser::from(move |s: &str| -> std::result::Result<String, Error> {
let reg = regex::Regex::new(r).unwrap();
match reg.is_match(s) {
true => Ok(s.to_owned()),
false => Err(Error::from(format!("not matches {}", r))),
}
})
}
On the code below, you can see that I forced the implementation of A for everything that implements AsRef<[T]> and Index[T], so B should implement it, because it implements both of these. However this is not the case. Why?
use std::convert::{AsMut, AsRef};
use std::ops::{Index, IndexMut};
use std::slice::SliceIndex;
pub trait A<T, I: SliceIndex<[T], Output = T>>:
AsRef<[T]> + Index<I, Output = T>
{
fn len(&self) -> usize;
}
impl<
T: AsRef<[T]> + Index<I, Output = T>,
I: SliceIndex<[T], Output = T>,
> A<T, I> for T
{
fn len(&self) -> usize {
self.as_ref().len()
}
}
struct B<'a, T>{
data: &'a mut [T]
}
impl<'a, T> AsRef<[T]> for B<'a, T> {
fn as_ref(&self) -> &[T] {
self.data
}
}
impl<'a, T, I: SliceIndex<[T], Output = T>> Index<I> for B<'a, T> {
type Output = T;
fn index(
&self,
v: I,
) -> &Self::Output {
&self.as_ref()[v]
}
}
fn something<'a, T>(r: &'a mut[T]) -> Box<dyn A<T, usize>> {
let a = B{data: r};
Box::new(a)
}
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=aa2353139f3d097e2497ed700d678ed3
Error:
error[E0277]: the trait bound `B<'_, T>: A<T, usize>` is not satisfied
--> src/lib.rs:46:5
|
46 | Box::new(a)
| ^^^^^^^^^^^ the trait `A<T, usize>` is not implemented for `B<'_, T>`
|
= note: required for the cast to the object type `dyn A<T, usize, Output = T>`
You need to introduce an additional type parameter on the blanket impl for A:
impl<T, I, V> A<T, I> for V
where
V: AsRef<[T]> + Index<I, Output = T>,
I: SliceIndex<[T], Output = T>,
{
fn len(&self) -> usize {
self.as_ref().len()
}
}
After fixing that, you'll get a lifetime error since the trait object returned in the box infers a 'static lifetime, so you'll need to bind it to the input slice's 'a lifetime.
fn something<'a, T>(r: &'a mut [T]) -> Box<dyn A<T, usize> + 'a> {
let a = B { data: r };
Box::new(a)
}
I designed a trait that can run things that are Lockable, that is, can be locked to produce Option<T>, and I implemented it for Arc<Mutex<Option<Box<T>>>>
use std::sync::{LockResult, PoisonError, MutexGuard, Arc, Mutex};
pub type LockableArc<T: ?Sized> = Arc<Mutex<Option<T>>>;
pub struct MutexGuardOptionRef<'a, T: ?Sized> {
pub mutex_guard: MutexGuard<'a, Option<Box<T>>>,
}
pub trait LockableOption<T: ?Sized>: Clone + Send {
fn lock(&self) -> LockResult<MutexGuardOptionRef<T>>;
}
pub trait Runnable<R: ?Sized, T: LockableOption<R> + Clone + Send + ?Sized> {
fn run(s: T) -> Result<(), ()>;
}
impl<T: ?Sized + Send> LockableOption<T> for LockableArc<Box<T>> {
fn lock(&self) -> LockResult<MutexGuardOptionRef<T>> {
unimplemented!()
}
}
pub trait A: Send{}
pub struct S{}
impl A for S{}
impl<R: A, T: LockableOption<R> + Clone + Send + 'static> Runnable<R, T>
for S
{
fn run(arc_rtsp: T) -> Result<(), ()> {
Ok(())
}
}
fn main() {
let r: LockableArc<Box<dyn A>> = Arc::new(Mutex::new(Some(Box::new(S{}))));
Runnable::run(r.clone());
//Runnable::<dyn A, LockableArc<Box<dyn A>>>::run(r.clone());
}
Playground
Error:
error[E0283]: type annotations needed
--> src/main.rs:39:5
|
14 | fn run(s: T) -> Result<(), ()>;
| ------------------------------- required by `Runnable::run`
...
39 | Runnable::run(r.clone());
| ^^^^^^^^^^^^^ cannot infer type
|
= note: cannot satisfy `_: Runnable<dyn A, Arc<Mutex<Option<Box<dyn A>>>>>`
I don't understand this error. First because I don't know what '_ is. It looks like it's the type passed to run as the only argument. So I tried to force the type:
Runnable::<dyn A, LockableArc<Box<dyn A>>>::run(r.clone());
but I get the same error. What is happening?
Runnable is a trait, so Runnable::run does not know which implementation of that trait to use. Use S::run instead.
S::run won't work because its implementation of Runnable requires that R: A whereas here R is dyn A. Just restrict R: ?Sized instead and you should be fine.
Therefore (playground):
use std::sync::{LockResult, PoisonError, MutexGuard, Arc, Mutex};
pub type LockableArc<T: ?Sized> = Arc<Mutex<Option<T>>>;
pub struct MutexGuardOptionRef<'a, T: ?Sized> {
pub mutex_guard: MutexGuard<'a, Option<Box<T>>>,
}
pub trait LockableOption<T: ?Sized>: Clone + Send {
fn lock(&self) -> LockResult<MutexGuardOptionRef<T>>;
}
pub trait Runnable<R: ?Sized, T: LockableOption<R> + Clone + Send + ?Sized> {
fn run(s: T) -> Result<(), ()>;
}
impl<T: ?Sized + Send> LockableOption<T> for LockableArc<Box<T>> {
fn lock(&self) -> LockResult<MutexGuardOptionRef<T>> {
unimplemented!()
}
}
pub trait A: Send{}
pub struct S{}
impl A for S{}
impl<R: ?Sized, T: LockableOption<R> + Clone + Send + 'static> Runnable<R, T>
for S
{
fn run(arc_rtsp: T) -> Result<(), ()> {
Ok(())
}
}
fn main() {
let r: LockableArc<Box<dyn A>> = Arc::new(Mutex::new(Some(Box::new(S{}))));
S::run(r.clone());
}
We can match a normal function with non-static parameter like this:
fn processor(data: &i32) -> &i32 {
data
}
fn process<'b>(data: &'b i32, processor: impl 'static + for<'a> Fn(&'a i32) -> &'a i32) -> &'b i32 {
processor(data)
}
fn main() {
let data = 1;
println!("data: {}", process(&data, processor));
}
Since async functions return anonymous futures, we cannot indicate that the lifetime of anonymous future is the same as the parameter:
use std::future::Future;
async fn processor(data: &i32) -> &i32 {
data
}
async fn process<'b, F>(data: &'b i32, processor: impl 'static + Fn(&i32) -> F) -> &'b i32
where
F: 'b + Future<Output = &'b i32>,
{
processor(data).await
}
async fn _main() {
let data = 1;
println!("data: {}", process(&data, processor).await);
}
The compiler will complain:
error[E0271]: type mismatch resolving `for<'r> <for<'_> fn(&i32) -> impl std::future::Future {processor} as std::ops::FnOnce<(&'r i32,)>>::Output == _`
--> src/lib.rs:16:26
|
7 | async fn process<'b, F>(data: &'b i32, processor: impl 'static + Fn(&i32) -> F) -> &'b i32
| ------- - required by this bound in `process`
...
16 | println!("data: {}", process(&data, processor).await);
| ^^^^^^^ expected bound lifetime parameter, found concrete lifetime
How can I match it?
You need to state that:
the closure accepts a reference with the same lifetime as the parameter.
the returned future returns a reference with the same lifetime as the parameter.
the returned future captures a reference with the same lifetime as the parameter.
async fn process<'b, F, Fut>(data: &'b i32, processor: F) -> &'b i32
where
F: Fn(&'b i32) -> Fut,
// ^^ [1]
F: 'static,
Fut: Future<Output = &'b i32> + 'b,
// ^^ [2] ^^ [3]
{
processor(data).await
}
I have this code:
use std::fmt::Debug;
struct S<A>
where
for<'a> A: Debug + 'a,
{
f: Box<Fn(A) -> i32>,
}
impl<A> S<A>
where
for<'a> A: Debug + 'a,
{
fn call(&self, a: A) {
println!("Return {:?}", (self.f)(a));
}
}
fn create<A>(f: Box<Fn(A) -> i32>) -> S<A>
where
for<'a> A: Debug + 'a,
{
S::<A> { f }
}
fn helper() {
let x = create::<&i32>(Box::new(|x: &i32| *x * 2));
let arg = 333;
x.call(&arg);
}
fn main() {
let x = helper();
}
It's failed to compile:
error[E0310]: the parameter type `A` may not live long enough
In code 2, I changed Fn(A) -> i32 to Fn(&A) -> i32, the code works.
...
f: Box<Fn(&A) -> i32>,
...
Since A is argument of Fn trait, it's a type that has Higher-Rank lifetime. It shouldn't be affected by the lifetime of struct S<A> .
But why can't code 1 be compiled?
How can I workaround it for borrow or non-borrow type A?
There is no easy way to make helper work in current Rust, even if you remove all the for<'a> A: Debug + 'a, bounds (which only further restricts what types A can be, whereas you want to allow more).
This is as simple as I can make your example:
struct S<A> {
f: Box<Fn(A) -> i32>,
}
impl<A> S<A> {
fn call(&self, a: A) {
println!("Return {:?}", (self.f)(a));
}
}
fn create<A>(f: Box<Fn(A) -> i32>) -> S<A> {
S { f }
}
fn helper() {
let x = create(Box::new(|x: &i32| *x * 2));
let arg = 333;
x.call(&arg);
}
fn main() {
helper();
}
The reason it doesn't work is that A "comes from the outside", and Rust can't infer that you want for<'a> S<&'a A>, it can't even talk about such a type.
Note that if let arg = 333; is placed above let x, this example does compile (because it infers a reference to arg specifically, not a for<'a>).
The closest you can get today is with an associated type on a trait with a lifetime parameter, e.g.:
// Emulating `type Type<'a>` by moving `'a` to the trait.
trait Apply<'a> {
type Type;
}
struct Plain<T>(std::marker::PhantomData<T>);
impl<'a, T> Apply<'a> for Plain<T> {
type Type = T;
}
struct Ref<T: ?Sized>(std::marker::PhantomData<T>);
impl<'a, T: ?Sized + 'a> Apply<'a> for Ref<T> {
type Type = &'a T;
}
struct S<A: for<'a> Apply<'a>> {
f: Box<for<'a> Fn(<A as Apply<'a>>::Type) -> i32>,
}
impl<A: for<'a> Apply<'a>> S<A> {
fn call<'a>(&self, a: <A as Apply<'a>>::Type) {
println!("Return {:?}", (self.f)(a));
}
}
fn create<A: for<'a> Apply<'a>>(
f: Box<for<'a> Fn(<A as Apply<'a>>::Type) -> i32>,
) -> S<A> {
S { f }
}
fn helper() {
let x = create::<Ref<i32>>(Box::new(|x: &i32| *x * 2));
let arg = 333;
x.call(&arg);
}
fn main() {
helper();
}
However, it turns out that this encoding hits https://github.com/rust-lang/rust/issues/52812, so it's not actually usable at the moment (and I'm not aware of an workaround).