Rust 智能指针问题

Rust smart pointer issue

提问人:안태찬 提问时间:10/31/2023 最后编辑:Chayim Friedman안태찬 更新时间:11/1/2023 访问量:69

问:

我正在尝试实现教会数字

//! representation of natural numbers using lambda calculus, named after  
//! Alonzo Church. Each Church numeral corresponds to a natural number `n`  
//! and is represented as a higher-order function that applies a given function `f` `n` times.  
//! ex) f(f(f(x))) is equal to number 3
use std::rc::Rc;

/// Church numerals are represented as higher-order functions that take a function `f`
pub type Church<T> = Rc<dyn Fn(Rc<dyn Fn(T) -> T>) -> Rc<dyn Fn(T) -> T>>;


/// Implement a function to convert a Church numeral to a usize type.
pub fn to_usize<T: 'static + Default>(n: Church<T>) -> usize {
    use std::cell::RefCell;

    let counter = Rc::new(RefCell::new(0));
    let counter_clone = Rc::clone(&counter);

    let result_func = n(Rc::new(move |x| {
        *counter_clone.borrow_mut() += 1;
        x
    }));

    let _ = result_func(Default::default());

    // Extract the value from the reference-counted cell
    let result = *counter.borrow();

    result
}

我正在尝试实现to_usize函数,它将给定的 Church 转换为其相应的值。
但是,计数器值不会更新,仍保持零。我认为 counter 和 counter_clone 有相同的参考......

Rust 智能指针 refcell

评论

0赞 Chayim Friedman 10/31/2023
你如何调用?你传递什么?to_usize()
0赞 Chayim Friedman 10/31/2023
请注意,您可以使用 Cell 而不是 .RefCell

答:

0赞 Aurel Bílý 11/1/2023 #1

我看不出代码的行为有问题:使用正确的输入(教会数字),它会产生预期的数字。

println!("0: {}", to_usize(Rc::new(|f: Rc<dyn Fn(usize) -> usize>| Rc::new(move |x| x))));
println!("1: {}", to_usize(Rc::new(|f: Rc<dyn Fn(usize) -> usize>| Rc::new(move |x| f(x)))));
println!("2: {}", to_usize(Rc::new(|f: Rc<dyn Fn(usize) -> usize>| Rc::new(move |x| f(f(x)))))));
println!("3: {}", to_usize(Rc::new(|f: Rc<dyn Fn(usize) -> usize>| Rc::new(move |x| f(f(f(x)))))));

按预期输出。也许您作为输入传递的函数不正确?03

另外,type 参数 of 不能准确地捕获您希望从 Church 数字中获得的内容,即它接收的函数在其输入中是多态的。相反,类型参数受调用站点的约束并在调用站点上选择,即使调用站点不使用它也是如此。因此,您跟踪“发生了多少次呼叫”的方式正在使用(正如@Chayim在评论中指出的那样,这可能是 ),这是令人惊讶的。to_usizeto_usizeRefCellCell

理想情况下,可以选择使用类型参数实例化 Church 数字,但这需要 Rust 不提供的 2 级多态性 ()。to_usizeusizefor <T> Rc<dyn Fn(T) -> T>

作为折衷方案,使用额外的特征约束:

pub fn to_usize<
    T: 'static + Default + std::ops::Add<usize, Output = T>
>(n: Church<T>) -> T {
    n(Rc::new(|x| x + 1))(Default::default())
}