[英]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
構造一個方法,它將為你構建它。
不幸的是, Decodable
和Decoder
現在真的沒有文檔記錄,所以當你試圖實現它們時,你必須依賴常識並檢查rustc --pretty=expanded
輸出。
使用最新的Rust版本更新編輯
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.