我正在尝试读取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
?更好的是,还有像记谱法这样的东西?
答案 0 :(得分:12)
对于你的第二个问题,是的,and_then()
上有Result
方法,但我担心它在这里不起作用,因为错误类型不同:read_to_string()
返回{ {1}}虽然Result<String, IoError>
返回json::decode()
,但您不能只将它们组合在一起 - 在Rust中没有通用的方法来描述类型的并集,因此您无法表达组合的错误类型
有计划轻松处理错误,this和this RFC涵盖了这些错误,因此将来这种情况可能会有所改善。
现在回答主要问题。
由于您在闭包参数中同时使用Result<T, DecoderError>
和取消引用模式,因此您将获得有关移除取消引用的编译错误。此方法返回一个迭代器,它将引用生成到向量中 - 满足iter()
绑定的内容。这意味着您无法通过此迭代器将值从向量中移出,因为无法将值移出引用。
但是,Iterator<&Vec<f64>>
模式意味着&v
应从引用中移出,即此封闭:
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]]
}
)或解构enums / tuples / etc类型有用,但int
不可隐式复制,因为它具有析构函数且不会发生解构这里。
首先,您可以将Vec<T>
完全保留在&
中(您也不需要将类型参数指定为&v
,因为它将从函数返回类型推断出来):< / p>
collect()
你可以这样做的原因是索引运算符被转换为trait方法调用,它自动解除引用它的目标。
但是,如果您使用points.iter().map(|v| [v[0], v[1]]).collect()
代替into_iter()
,则会更好地表达意图:
iter()
points.into_iter().map(|v| [v[0], v[1]]).collect()
上的{p> into_iter()
返回一个迭代器,它产生Vec<T>
,而不是T
,&T
,因此iter()
的类型为v
{1}}。 Vec<f64>
会消耗其目标,但由于此调用后未使用into_iter()
,因此这样做是安全的,并且它更好地表达了points
转换为结果的事实。
但还有更好的方法。 JSON解码器不支持反序列化像points
这样的静态大小的数组,因为它需要在泛型参数中支持数字,而Rust还没有它们。但您可以随时编写自己的类型并为其实现[f64, ..2]
:
Decodable
(试试here)
现在,如果您需要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]
结构添加一个方法,为您构建它。
不幸的是,Decodable
和Decoder
现在真的没有记录,因此当您尝试实施时,您必须依赖常识并检查Point
输出。
编辑