我正在尝试为Expat XML解析器实现Rust包装器。我包装了start_element,end_element回调,它们在简单的情况下工作正常(例如,只计算XML元素),如下所示:
struct Expat {
parser: expat::XML_Parser
}
type StartHandler = @fn(tag: &str, attrs: &[~str]);
type EndHandler = @fn(tag: &str);
type TextHandler = @fn(text: &str);
struct Handlers {
start_handler: StartHandler,
end_handler: EndHandler,
text_handler: TextHandler
}
impl Expat {
pub fn handlers(&self, start_handler: StartHandler, end_handler: EndHandler, text_handler: TextHandler) {
let handlers = @Handlers {
start_handler: start_handler,
end_handler: end_handler,
text_handler: text_handler
};
// How to do this properly?
unsafe { cast::bump_box_refcount(handlers) };
expat::XML_SetUserData(self.parser, unsafe { cast::transmute(&*handlers) });
}
我可以将简单的托管闭包传递给处理程序()并让它们更新@mut uint值。
现在我想在回调中维护当前的XPath并遇到问题:
let mut xpath: ~[~str] = ~[];
let xpath_start_handler: @fn(&str, &[~str]) = |tag: &str, _attrs: &[~str]| {
vec::push(&xpath, tag.to_owned());
println(fmt!(" start: %?", xpath));
};
let xpath_end_handler: @fn(&str) = |tag: &str| {
println(fmt!(" end: %?", xpath));
let top = vec::pop(&xpath);
if top != tag.to_owned() {
fail!(fmt!("expected end tag: %s, received end tag: %s", top, tag));
}
};
let xpath_text_handler: @fn(&str) = |_text: &str| {
};
expat.handlers(xpath_start_handler, xpath_end_handler, xpath_text_handler);
编译器说独特的向量xpath被移动到xpath_start_handler闭包中,无法在xpath_end_closure中访问。
所以我的问题是在多个托管闭包中保持可变状态的最佳方法是什么?
答案 0 :(得分:1)
应该管理共享框,而不是唯一:
let state: @mut ~[~str] = @mut ~[];
let push: @fn(~str) = |x| {
vec::push(state, x);
};
let pop: @fn() -> ~str = || {
vec::pop(state)
};
let count: @fn() -> uint = || {
(&*state).len()
};
push(~"ho");
push(~"hey");
println(fmt!("%?", count()));
println(pop());
println(pop());
println(fmt!("%?", count()));
此外,可变的唯一框与可变管理框的工作方式略有不同。