【问题标题】:How to write a proper map function with Rust?如何用 Rust 编写正确的 map 函数?
【发布时间】:2014-04-04 17:04:41
【问题描述】:

使用如下链表定义:

enum List<T> {
    Nil,
    Cons(T, ~List<T>)
}

我正在尝试编写一个映射函数(即对列表的每个元素应用一个操作并返回一个新列表)。我正在尝试使用本教程和其他不同地方(例如Rust for Rubyists)中给出的指南,因此我尝试尽可能使用值和借用指针而不是拥有的指针。这导致我得到以下函数定义:

fn map<T1, T2>(f: |T1| -> T2, xs: &List<T1>) -> ~List<T2> { ... }

我认为这是有道理的;转换器函数对值起作用,列表参数是借来的指针。我返回一个拥有的指针,因为我需要将一个值与递归调用的返回值相结合。

现在,让我们看看身体:

fn map<T1, T2>(f: |T1| -> T2, xs: &List<T1>) -> ~List<T2> {
    match xs {
        &Nil => ~Nil,
        &Cons(x, ~ref rest) => ~Cons(f(x), map(f, rest))
    }
}

这是我的第一次尝试; ~ref 语法有点不直观,但我在教程中找到了它。此实现无法编译。

demo.rs:25:15: 25:16 error: cannot bind by-move and by-ref in the same pattern
demo.rs:25         &Cons(x, ~ref rest) => ~Cons(f(x), map(f, rest))
                         ^
demo.rs:25:19: 25:27 note: by-ref binding occurs here
demo.rs:25         &Cons(x, ~ref rest) => ~Cons(f(x), map(f, rest))
                             ^~~~~~~~
error: aborting due to previous error

好吧,显然在进行模式匹配时,内部模式必须具有相同的移动语义,不能混合匹配。让我们尝试在x 模式之前添加ref:

fn map<T1, T2>(f: |T1| -> T2, xs: &List<T1>) -> ~List<T2> {
    match xs {
        &Nil => ~Nil,
        &Cons(ref x, ~ref rest) => ~Cons(f(x), map(f, rest))
    }
}

demo.rs:25:44: 25:45 error: mismatched types: expected `T1` but found `&T1` (expected type parameter but found &-ptr)
demo.rs:25         &Cons(ref x, ~ref rest) => ~Cons(f(x), map(f, rest))
                                                      ^
error: aborting due to previous error

再次出错;模式匹配没问题,但是我没有正确的类型来调用我的闭包。使用语法f(*x) 是非法的,所以我需要更改我的闭包类型以接收借来的指针:

fn map<T1, T2>(f: |&T1| -> T2, xs: &List<T1>) -> ~List<T2> {
    match xs {
        &Nil => ~Nil,
        &Cons(ref x, ~ref rest) => ~Cons(f(x), map(f, rest))
    }
}

这个版本终于可以工作了。

谁能告诉我地图在 Rust 中是否应该是这样的?

【问题讨论】:

    标签: pointers rust


    【解决方案1】:

    这是一张可以接受的地图,但我有几个 cmets。

    首先,从map() 的类型签名你知道f 需要采用&amp;T1 而不是T1。这是因为采用 T1 意味着它必须将值移动到闭包中,但它在借用的 List&lt;T1&gt; 上运行,因此无法移动它。

    其次,您的地图不需要返回~List&lt;T2&gt;,它可以只返回List&lt;T2&gt;,您可以自己将递归调用包装在~ 指针中。这看起来像

    fn map<T,U>(f: |&T| -> U, xs: &List<T>) -> List<U> {
        match *xs {
            Nil => Nil,
            Cons(ref x, ~ref rest) => Cons(f(x), ~map(f, rest))
        }
    }
    

    第三,实现这一点的最好方法根本不是写map(),而是写iter(),它产生一个实现Iterator&lt;&amp;T1&gt;的类型。迭代器隐式支持映射。然后您还需要实现 FromIterator 以允许您将映射结果转换回 List。


    这是一个迭代器的实现和示例用法:

    #[deriving(Show)]
    pub enum List<T> {
        Nil,
        Cons(T, ~List<T>)
    }
    
    impl<T> List<T> {
        pub fn iter<'a>(&'a self) -> Items<'a, T> {
            Items { list: self }
        }
    }
    
    pub struct Items<'a, T> {
        list: &'a List<T>
    }
    
    impl<'a, T> Iterator<&'a T> for Items<'a, T> {
        fn next(&mut self) -> Option<&'a T> {
            match *self.list {
                Nil => None,
                Cons(ref x, ~ref rest) => {
                    self.list = rest;
                    Some(x)
                }
            }
        }
    }
    
    impl<A> FromIterator<A> for List<A> {
        fn from_iter<T: Iterator<A>>(mut iterator: T) -> List<A> {
            match iterator.next() {
                None => Nil,
                Some(x) => Cons(x, ~FromIterator::from_iter(iterator))
            }
        }
    }
    
    fn main() {
        let x = Cons(1u, ~Cons(2u, ~Cons(3u, ~Nil)));
        println!("{}", x);
        // prints Cons(1, Cons(2, Cons(3, Nil)))
        let y: List<uint> = x.iter().map(|&x| x*2).collect();
        println!("{}", y);
        // prints Cons(2, Cons(4, Cons(6, Nil)))
    }
    

    【讨论】:

    • 感谢您的详尽回答;我最感兴趣的是玩 Rust 如何处理所有权而不是实际实现 map,但希望您的答案的第二部分将来对我有用。干杯!
    猜你喜欢
    • 2018-01-28
    • 2017-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-09
    • 2020-08-16
    相关资源
    最近更新 更多