我是 Rust 的新手,在處理閉包時遇到了一些障礙,無論是從函式或方法回傳它們時,還是需要將它們存盤為結構欄位時。
讓我們從有效的開始:
fn build_func(b: usize) -> impl Fn(usize) -> usize {
move |a| a b
}
struct Foo<F>
where
F: Fn(usize) -> usize,
{
pub foo: F,
}
impl<F> Foo<F>
where
F: Fn(usize) -> usize,
{
fn new(foo: F) -> Self {
Self { foo }
}
}
fn main() {
let foo1 = Foo { foo: |a| a 1 };
let foo2 = Foo { foo: build_func(2) };
let foo_func = build_func(3);
let foo3 = Foo { foo: foo_func };
}
這按預期作業,并且在結構外部構建的閉包型別與Foo
.
我想達到同樣的效果,但只需將閉包的創建隱藏在自身內部impl
即可Foo
。
我測驗了這些替代方案,但沒有一個主題編譯:
struct Foo2<F>
where
F: Fn(usize) -> usize,
{
pub foo: F,
}
impl<F> Foo2<F>
where
F: Fn(usize) -> usize,
{
fn new() -> Self {
let foo = build_func(1);
Self { foo }
}
}
struct Foo3<F>
where
F: Fn(usize) -> usize,
{
pub foo: F,
}
impl<F> Foo3<F>
where
F: Fn(usize) -> usize,
{
fn new() -> Self {
let foo = |a| a 1;
Self { foo }
}
}
struct Foo4<F>
where
F: Fn(usize) -> usize,
{
pub foo: F,
}
impl<F> Foo4<F>
where
F: Fn(usize) -> usize,
{
fn new() -> Self {
let foo = Self::build_func(1);
Self { foo }
}
fn build_func(b: usize) -> F {
move |a| a b
}
}
struct Foo5<F>
where
F: Fn(usize) -> usize,
{
pub foo: F,
}
impl<F> Foo5<F>
where
F: Fn(usize) -> usize,
{
fn new() -> Self {
let foo = Self::build_func(1);
Self { foo }
}
fn build_func(b: usize) -> impl Fn(usize) -> usize {
move |a| a b
}
}
我知道每個閉包都有自己的不透明和不同的型別,但是我不明白為什么另一方面的初始實作Foo
起作用。
通過閱讀此處接受的答案,我似乎明白,在這種情況下,唯一的選擇是裝箱,但我仍然沒有完全理解。
通過結合盒裝閉包和特征別名(我知道這不是“真正的”特征別名),我想出了這個:
trait Func: Fn(usize) -> usize {}
impl<T> Func for T where T: Fn(usize) -> usize {}
struct Foo6 {
pub foo: Box<dyn Func>,
}
impl Foo6 {
fn new() -> Self {
let foo = Self::build_func(1);
Self { foo: Box::new(foo) }
}
fn build_func(b: usize) -> impl Func {
move |a| a b
}
}
fn main() {
let foo = Foo6::new();
println!("{}", (foo.foo)(1));
}
但我想知道是否有可能獲得未裝箱的版本。
任何澄清將不勝感激!
uj5u.com熱心網友回復:
這段代碼中的問題:
impl<F> Foo2<F>
where
F: Fn(usize) -> usize,
{
fn new() -> Self {
let foo = build_func(1);
Self { foo }
}
}
就是要呼叫它,用戶代碼需要撰寫如下內容:
let foo2 = <Foo2<?>>::new();
并在那里指定某種型別。即使型別沒有顯式鍵入,也應該在那里解決(型別省略主要是語法糖)。但是存盤值的型別是在該函式內部決定的,在對 的呼叫中build_func(1)
,因此用戶在那里沒有可使用的型別,并且無法呼叫該函式。
我的建議是只寫一個自由函式:
fn new_foo2() -> Foo2<impl Fn(usize) -> usize> {
let foo = build_func(1);
Foo2 { foo }
}
現在泛型型別是impl
函式回傳型別中的一個,即由函式代碼決定的特殊的不透明泛型。所以這行得通。
如果你真的,真的想寫Foo2::new
,你可以在一個虛擬的非泛型impl Foo2
塊中撰寫函式實作。通常是這樣,impl Foo2<()>
但型別()
不滿足您的約束,但您可以使用任何其他虛擬型別:
impl Foo2<fn(usize)->usize> {
fn new() -> Foo2<impl Fn(usize) -> usize> {
let foo = build_func(1);
Foo2 { foo }
}
}
(請注意,這new()
不會回傳Self
,因為泛型型別不正確。)
現在你至少可以寫:
let foo2 = Foo2::new();
uj5u.com熱心網友回復:
這里的不同之處在于您是Self
從new()
. 里面impl<F> Foo<F>
,Self
是指Foo<F>
。AndF
是一個泛型引數 - 你不能構建 type 的閉包F
,因為它是你的呼叫者決定它的型別,而不是你。
您的第一個版本有效,因為它告訴編譯器“我將回傳一些實作的型別Fn
;我將把它留給您推斷到底是什么”。另一方面,第二個版本都是“給定任何F
實作的型別Fn
,我會給你一個該型別的實體”。這當然是不可能的。
相反,您希望編譯器也在這里推斷使用的型別。最好的解決方案也是在impl Trait
這里使用。但是impl Trait
在回傳型別以外的位置是不穩定的。它看起來像(游樂場):
#![feature(type_alias_impl_trait)]
type InnerFn = impl Fn(usize) -> usize;
struct Foo {
pub foo: InnerFn,
}
impl Foo {
fn new() -> Self {
let foo = Self::build_func(1);
Self { foo }
}
fn build_func(b: usize) -> InnerFn {
move |a| a b
}
}
另一個(相當老套的)解決方案是有一個通用引數,但不使用它,而是使用impl Trait
in new()
。為了不要求呼叫者指定冗余引數(因為它沒有被使用而無法推斷),我們可以使用標記型別,通常是()
. 這要求我們F: Fn(usize) -> usize
從結構中洗掉邊界并將其僅放在 impl 上,但是無論如何這是一個很好的樣式(游樂場):
struct Foo<F> {
pub foo: F,
}
impl Foo<()> {
fn new() -> Foo<impl Fn(usize) -> usize> {
let foo = Self::build_func(1);
Foo { foo }
}
fn build_func(b: usize) -> impl Fn(usize) -> usize {
move |a| a b
}
}
最后一個解決方案確實是裝箱,但你不需要新的特性 - 你可以Fn
直接使用(游樂場):
struct Foo {
pub foo: Box<dyn Fn(usize) -> usize>,
}
impl Foo {
fn new() -> Self {
let foo = Self::build_func(1);
Self { foo: Box::new(foo) }
}
fn build_func(b: usize) -> impl Fn(usize) -> usize {
move |a| a b
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/470420.html
下一篇:在XAML中使用泛型:WPF