Upgrade to Pro — share decks privately, control downloads, hide ads and more …

RustのReturn-position impl trait in trait (RPITI...

RustのReturn-position impl trait in trait (RPITIT) の実装を雑に見てみる

TaKO8Ki

June 05, 2024
Tweet

More Decks by TaKO8Ki

Other Decks in Programming

Transcript

  1. RPITITとは? fn combine_vecs( v: Vec<i32>, u: Vec<i32>, ) -> impl

    Iterator<Item=i32> { v.into_iter().chain(u.into_iter()).cycle() }
  2. RPITITとは? struct Foo { items: Vec<Bar> } #[derive(Clone)] struct Bar;

    trait Baz { fn items(&self) -> impl Iterator<Item = Bar>; } impl Baz for Foo { fn items(&self) -> impl Iterator<Item = Bar> { self.items.iter().cloned() } }
  3. Async fn in trait (AFIT) pub trait Trait { async

    fn method(&self); } // desugars to pub trait Trait { fn method(&self) -> impl Future<Output = ()>; }
  4. ASTからHIRへのlowering pub struct OpaqueTy<'hir> { pub generics: &'hir Generics<'hir>, pub

    bounds: GenericBounds<'hir>, pub origin: OpaqueTyOrigin, pub lifetime_mapping: &'hir [(&'hir Lifetime, LocalDefId)], pub in_trait: bool, pub precise_capturing_args: Option<(&'hir [PreciseCapturingArg<'hir>], Span)>, }
  5. ASTからHIRへのlowering pub struct OpaqueTy<'hir> { pub generics: &'hir Generics<'hir>, pub

    bounds: GenericBounds<'hir>, pub origin: OpaqueTyOrigin, pub lifetime_mapping: &'hir [(&'hir Lifetime, LocalDefId)], pub in_trait: bool, pub precise_capturing_args: Option<(&'hir [PreciseCapturingArg<'hir>], Span)>, }
  6. ASTからHIRへのlowering let opaque_ty_item = hir::OpaqueTy { generics: this.arena.alloc(hir::Generics { params:

    generic_params, predicates: &[], has_where_clause_predicates: false, where_clause_span: this.lower_span(span), span: this.lower_span(span), }), bounds, origin, lifetime_mapping, in_trait, precise_capturing_args, }; compiler/rustc_ast_lowering/src/lib.rs#L1738-L1751
  7. HIRからtyへのlowering (traits) trait NewIntoIterator { type Item; fn into_iter(self) ->

    impl Iterator<Item = Self::Item>; } // 下のようになる (例) trait NewIntoIterator { type Item; type A: Iterator<Item = Self::Item>; fn into_iter(self) -> <Self as NewIntoIterator>::A; }
  8. HIRからtyへのlowering (traits) trait NewIntoIterator { type Item; fn into_iter(self) ->

    impl Iterator<Item = Self::Item>; } // 下のようになる (例) trait NewIntoIterator { type Item; type A: Iterator<Item = Self::Item>; fn into_iter(self) -> <Self as NewIntoIterator>::A; }
  9. HIRからtyへのlowering (impls) impl NewIntoIterator for Vec<u32> { type Item =

    u32; fn into_iter(self) -> impl Iterator<Item = Self::Item> { self.into_iter() } } // 下のようになる (例) impl NewIntoIterator for Vec<u32> { type Item = u32; type A = impl Iterator<Item = Self::Item>; fn into_iter(self) -> <Self as NewIntoIterator>::A { self.into_iter() } }
  10. HIRからtyへのlowering (impls) impl NewIntoIterator for Vec<u32> { type Item =

    u32; fn into_iter(self) -> impl Iterator<Item = Self::Item> { self.into_iter() } } // 下のようになる (例) impl NewIntoIterator for Vec<u32> { type Item = u32; type A = impl Iterator<Item = Self::Item>; fn into_iter(self) -> <Self as NewIntoIterator>::A { self.into_iter() } }
  11. HIRからtyへのlowering (impls) を少し深堀り 親の関数から引き継いでくれば良い 
 // Copy visility of the

    containing function. trait_assoc_ty .visibility(tcx.visibility(fn_def_id)); // Copy defaultness of the containing function. trait_assoc_ty .defaultness(tcx.defaultness(fn_def_id)); compiler/rustc_ty_utils/src/assoc.rs#L280-L285
  12. RPITITでdynが動かないのはなぜか? trait NotObjectSafe { const CONST: i32 = 1; //

    ERROR: cannot have associated const fn foo() {} // ERROR: associated function without Sized fn returns(&self) -> Self; // ERROR: Self in return type fn typed<T>(&self, x: T) {} // ERROR: has generic type parameters fn nested(self: Rc<Box<Self>>) {} // ERROR: nested receiver not yet supported }
  13. RPITITでdynが動かないのはなぜか? trait NotObjectSafe { fn foo() {} } struct S;

    impl NotObjectSafe for S { fn foo() { } } fn main() { let obj: Box<dyn NotObjectSafe> = Box::new(S); }
  14. RPITITでdynが動かないのはなぜか? error[E0038]: the trait `NotObjectSafe` cannot be made into an

    object --> src/main.rs:12:14 | 12 | let obj: Box<dyn NotObjectSafe> = Box::new(S); // ERROR | ^^^^^^^^^^^^^^^^^^^^^^ `NotObjectSafe` cannot be made into an object | note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> --> src/main.rs:2:8 | 1 | trait NotObjectSafe { | ------------- this trait cannot be made into an object... 2 | fn foo() {} // ERROR: associated function without Sized | ^^^ ...because associated function `foo` has no `self` parameter = help: only type `S` implements the trait, consider using it directly instead
  15. RPITITでdynが動かないのはなぜか? error[E0038]: the trait `NotObjectSafe` cannot be made into an

    object --> src/main.rs:12:14 | 12 | let obj: Box<dyn NotObjectSafe> = Box::new(S); // ERROR | ^^^^^^^^^^^^^^^^^^^^^^ `NotObjectSafe` cannot be made into an object | note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> --> src/main.rs:2:8 | 1 | trait NotObjectSafe { | ------------- this trait cannot be made into an object... 2 | fn foo() {} // ERROR: associated function without Sized | ^^^ ...because associated function `foo` has no `self` parameter = help: only type `S` implements the trait, consider using it directly instead
  16. RPITITでdynを動かしたい リリース記事ではtrait-variant crateがdynamic dispatchに対応する予定ら しい。 > We plan to provide

    utilities that enable dynamic dispatch in an upcoming version of the trait-variant crate. ref: https://blog.rust-lang.org/2023/12/21/async-fn-rpit-in-traits.html
  17. RPITITでdynを動かしたい pub trait AsyncIter { type Item; async fn next(&mut

    self) -> Option<Self::Item>; async fn size_hint(&self) -> Option<usize>; }
  18. RPITITでdynを動かしたい pub trait AsyncIter { type Item; // async fn

    next(&mut self) -> Option<Self::Item>; fn next(&mut self) -> impl Future<Output = Option<Self::Item>>; // async fn size_hint(&self) -> Option<usize>; fn size_hint(&self) -> impl Future<Output = Option<usize>>; }
  19. RPITITでdynを動かしたい pub trait AsyncIter { type Item; type Next: Future<Output

    = Option<Self::Item>>; // fn next(&mut self) -> impl Future<Output = Option<Self::Item>> fn next(&mut self) -> impl Future<Output = Option<Self::Item>>; type SizeHint: Future<Output = Option<usize>>; // fn size_hint(&self) -> impl Future<Output = Option<usize>>; fn size_hint(&self) -> Option<usize>; }
  20. RPITITでdynを動かしたい pub struct DynAsyncIter <'data, Item> { fatptr: FatPtr<'data, Item>,

    } trait ErasedAsyncIter { type Item; fn next<'me>(&'me mut self) -> Pin<Box<dyn Future<Output = Option<Self::Item>> + 'me>>; fn size_hint<'me>(&'me self) -> Pin<Box<dyn Future<Output = Option<usize>> + 'me>>; }
  21. RPITITでdynを動かしたい impl<'data, Item> DynAsyncIter<'data, Item> { pub fn new<T>(value: T)

    -> DynAsyncIter<'data, Item> where T: AsyncIter<Item = Item> + 'data, Item: 'data, { let b: Box<dyn ErasedAsyncIter<Item = Item>> = Box::new(value); let raw: *mut dyn ErasedAsyncIter<Item = Item> = Box::into_raw(b); unsafe { DynAsyncIter { fatptr: FatPtr::new(raw).tagged(), } } } // .. }
  22. RPITITでdynを動かしたい union FatPtr<'data, Item> { raw: *mut (dyn ErasedAsyncIter<Item =

    Item> + 'data), usizes: (usize, usize), } impl<'data, Item> FatPtr<'data, Item> { // ... unsafe fn tagged(self) -> Self { let (data, vtable) = self.usizes; FatPtr { usizes: (data | 1, vtable), } } } usizesフィールドの1番目の要素 としてvtableへのポインタが 入っている
  23. RPITITでdynを動かしたい #[trait_variant (dyn)] pub trait AsyncIter { type Item; async

    fn next(&mut self) -> Option<Self::Item>; async fn size_hint(&self) -> Option<usize>; } trait_variant(dyn)によってこ れらのtrait/structが実装され るようにするのが現状の方針ら しい
  24. まとめ • ASTから hir::ItemKind::Opaque にloweringされるところから始まり、RPITIT 動きが何となく理解できた。
 • RPITITやasync fnはobject safeではないのでdynが使えない。

    • dynamic dispatchを使うためには今のところ async-trait proc macro が必要だが、将来的に trait-variant が対応する予定がある。