繁体   English   中英

阅读Rust中的Json

[英]Read Json in Rust

我试图在Rust中读取Json文件的内容。 此文件包含平面中的点阵列,如[[1, 2], [3.5, 2.7], [0, -2.1]]

我第一次尝试实现的是

extern crate serialize;
use serialize::json;
use std::io::File;

fn read_points() -> Vec<[f64, ..2]> {
  let contents = File::open(&Path::new("../points.json")).read_to_string().unwrap();
  let points: Vec<Vec<f64>> = json::decode(contents.as_slice()).unwrap();

  points.iter().map(|&v| [v[0], v[1]]).collect::<Vec<[f64, ..2]>>()
}

现在我有两个问题。

首先,我得到一个编译错误

error: cannot move out of dereference of `&`-pointer

这似乎意味着我的地图操作不安全。 作为Rust的一个完整的新手,对我来说并不明显&v驻留在内存中。 理想情况下,我想访问支持Vec<f64>的底层数组,甚至更好,避免将内部向量直接分配给json到Vec<[f64, ..2]>

第二 - 但不太重要 - 有两个丑陋的unwrap 现在,我明白读取文件和解析json都可能会失败。 有没有办法轻松组合Result实例,例如Scala中的flatmap或Haskell中的bind 甚至更好,像记号?

关于第二个问题,是的,有and_then()的方法Result ,但我担心,因为错误类型是不同的它不会在这里工作: read_to_string()返回Result<String, IoError>json::decode()返回Result<T, DecoderError>你不能只将它们组合起来-有描述的鲁斯特类型的工会没有通用的方法,这样你就不能表达组合错误类型。

有计划简化错误处理, 这个这个 RFC涵盖了它们,所以这种情况可能会在未来得到改善。

现在回答主要问题。

因为你在闭包参数中同时使用了iter()和dereference模式,所以你得到了关于移出dereference的编译错误。 这个方法返回一个迭代器,它产生向量的引用 - 满足Iterator<&Vec<f64>> bound的东西。 这意味着您无法通过此迭代器将值从向量移出,因为无法将值移出引用。

但是, &v pattern意味着v 应该从引用中移出,也就是说,这个闭包:

|&v| [v[0], v[1]]  // v is Vec<f64>

相当于这一个:

|r| {              // r is &Vec<f64>
    let v = *r;    // v is Vec<f64>
    [v[0], v[1]]
}

此模式仅对可隐式复制的类型(如int )或解构enums / tuples / etc类型有用,但Vec<T>不可隐式复制,因为它具有析构函数且此处不会发生解构。

首先,你可以离开&&v完全(你也不需要指定类型参数collect()因为它会从函数的返回类型推断):

points.iter().map(|v| [v[0], v[1]]).collect()

您可以这样做的原因是索引运算符被转换为特征方法调用,该调用自动解除引用其目标。

但是,如果使用into_iter()而不是iter() ,则会更好地表达意图:

points.into_iter().map(|v| [v[0], v[1]]).collect()

Vec<T>上的into_iter()返回一个产生T的迭代器,而不是像iter()那样的&T ,所以这里的v将是Vec<f64>类型。 into_iter()它的目标,但由于在此调用之后没有使用points ,所以这样做是安全的,并且它更好地表达了points被转换为结果的事实。

但还有更好的方法。 JSON解码器不支持反序列化像[f64, ..2]这样的静态大小的数组[f64, ..2]因为它需要在泛型参数中支持数字,而Rust还没有它们。 但是你可以随时编写自己的类型并为它实现Decodable

extern crate serialize;

use serialize::{Decoder, Decodable};
use serialize::json;

#[deriving(Show)]
struct Point(f64, f64);

impl Decodable for Point {
    fn decode<D: Decoder>(d: &mut D) -> Result<Point, D::Error> {
        d.read_tuple(2, |d| {
            d.read_tuple_arg(0, |d| d.read_f64()).and_then(|e1|
                d.read_tuple_arg(1, |d| d.read_f64()).map(|e2|
                    Point(e1, e2)
                )
            )
        })
    }
}

fn main() {
    let s = "[[1, 2], [3.5, 2.7], [0, -2.1]]";
    let v: Vec<Point> = json::decode(s).unwrap();
    println!("{}", v);
}

在这里试试)

现在,如果你需要[f64, ..2]你总是可以为Point构造一个方法,它将为你构建它。

不幸的是, DecodableDecoder现在真的没有文档记录,所以当你试图实现它们时,你必须依赖常识并检查rustc --pretty=expanded输出。

使用最新的Rust版本更新编辑

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM