阅读Rust中的Json

And*_*rea 5 json 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]>>()
}
Run Code Online (Sandbox Code Playgroud)

现在我有两个问题.

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

error: cannot move out of dereference of `&`-pointer
Run Code Online (Sandbox Code Playgroud)

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

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

Vla*_*eev 13

对于你的第二个问题,是的,有and_then()方法Result,但我担心它不会在这里工作,因为错误类型是不同的:read_to_string()返回Result<String, IoError>json::decode()返回Result<T, DecoderError>,你不能只是组合它们 - 没有通用的方法来描述Rust中的类型联合,因此您无法表达组合错误类型.

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

现在回答主要问题.

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

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

|&v| [v[0], v[1]]  // v is Vec<f64>
Run Code Online (Sandbox Code Playgroud)

相当于这一个:

|r| {              // r is &Vec<f64>
    let v = *r;    // v is Vec<f64>
    [v[0], v[1]]
}
Run Code Online (Sandbox Code Playgroud)

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

首先,您可以完全&省略&v(您也不需要指定类型参数,collect()因为它将从函数返回类型推断):

points.iter().map(|v| [v[0], v[1]]).collect()
Run Code Online (Sandbox Code Playgroud)

你可以这样做的原因是索引操作符被转换为trait方法调用,它自动解除引用它的目标.

但是,如果您使用into_iter()而不是以下情况,您会更好地表达意图iter():

points.into_iter().map(|v| [v[0], v[1]]).collect()
Run Code Online (Sandbox Code Playgroud)

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

但还有更好的方法.JSON解码器不支持反序列化静态大小的数组,[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);
}
Run Code Online (Sandbox Code Playgroud)

(在这里试试)

现在,如果您需要,[f64, ..2]您可以随时添加一个方法来Point构造它,为您构建它.

不幸的是,DecodableDecoder真的现在未公开,所以你必须依靠常识和检查rustc --pretty=expanded输出,当你尝试实现它们.

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