簡體   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