auto merge of #8141 : graydon/rust/foreach-in-sketch, r=brson
This is a preliminary implementation of `for ... in ... { ...}` using a transitionary keyword `foreach`. Codesize seems to be a little bit down (10% or less non-opt) and otherwise it seems quite trivial to rewrite lambda-based loops to use it. Once we've rewritten the codebase away from lambda-based `for` we can retarget that word at the same production, snapshot, rewrite the keywords in one go, and expire `foreach`. Feedback welcome. It's a desugaring-based approach which is arguably something we should have been doing for other constructs before. I apologize both for the laziness associated with doing it this way and with any sense that I'm bending rules I put in place previously concerning "never doing desugarings". I put the expansion in `expand.rs` and would be amenable to the argument that the code there needs better factoring / more helpers / to move to a submodule or helper function. It does seem to work at this point, though, and I gather we'd like to get the shift done relatively quickly.
This commit is contained in:
commit
6296dc0d73
@ -239,6 +239,8 @@ impl CFGBuilder {
|
||||
expr_exit
|
||||
}
|
||||
|
||||
ast::expr_for_loop(*) => fail!("non-desugared expr_for_loop"),
|
||||
|
||||
ast::expr_loop(ref body, _) => {
|
||||
//
|
||||
// [pred]
|
||||
|
@ -583,6 +583,8 @@ impl<'self, O:DataFlowOperator> PropagationContext<'self, O> {
|
||||
copy_bits(new_loop_scope.break_bits, in_out);
|
||||
}
|
||||
|
||||
ast::expr_for_loop(*) => fail!("non-desugared expr_for_loop"),
|
||||
|
||||
ast::expr_loop(ref blk, _) => {
|
||||
//
|
||||
// (expr) <--+
|
||||
|
@ -503,6 +503,7 @@ fn visit_expr(expr: @expr, (this, vt): (@mut IrMaps, vt<@mut IrMaps>)) {
|
||||
this.add_live_node_for_node(expr.id, ExprNode(expr.span));
|
||||
visit::visit_expr(expr, (this, vt));
|
||||
}
|
||||
expr_for_loop(*) => fail!("non-desugared expr_for_loop"),
|
||||
expr_binary(_, op, _, _) if ast_util::lazy_binop(op) => {
|
||||
this.add_live_node_for_node(expr.id, ExprNode(expr.span));
|
||||
visit::visit_expr(expr, (this, vt));
|
||||
@ -1057,6 +1058,8 @@ impl Liveness {
|
||||
self.propagate_through_loop(expr, Some(cond), blk, succ)
|
||||
}
|
||||
|
||||
expr_for_loop(*) => fail!("non-desugared expr_for_loop"),
|
||||
|
||||
// Note that labels have been resolved, so we don't need to look
|
||||
// at the label ident
|
||||
expr_loop(ref blk, _) => {
|
||||
@ -1487,6 +1490,7 @@ fn check_expr(expr: @expr, (this, vt): (@Liveness, vt<@Liveness>)) {
|
||||
expr_paren(*) | expr_fn_block(*) | expr_path(*) | expr_self(*) => {
|
||||
visit::visit_expr(expr, (this, vt));
|
||||
}
|
||||
expr_for_loop(*) => fail!("non-desugared expr_for_loop")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -435,6 +435,8 @@ impl mem_categorization_ctxt {
|
||||
ast::expr_inline_asm(*) => {
|
||||
return self.cat_rvalue_node(expr, expr_ty);
|
||||
}
|
||||
|
||||
ast::expr_for_loop(*) => fail!("non-desugared expr_for_loop")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -487,6 +487,8 @@ impl VisitContext {
|
||||
self.consume_block(blk, visitor);
|
||||
}
|
||||
|
||||
expr_for_loop(*) => fail!("non-desugared expr_for_loop"),
|
||||
|
||||
expr_unary(_, _, lhs) => {
|
||||
if !self.use_overloaded_operator(
|
||||
expr, lhs, [], visitor)
|
||||
|
@ -5016,6 +5016,8 @@ impl Resolver {
|
||||
}
|
||||
}
|
||||
|
||||
expr_for_loop(*) => fail!("non-desugared expr_for_loop"),
|
||||
|
||||
expr_break(Some(label)) | expr_again(Some(label)) => {
|
||||
match self.search_ribs(self.label_ribs, label, expr.span,
|
||||
DontAllowCapturingSelf) {
|
||||
|
@ -2266,7 +2266,7 @@ pub fn register_fn_fuller(ccx: @mut CrateContext,
|
||||
sp: span,
|
||||
sym: ~str,
|
||||
node_id: ast::NodeId,
|
||||
node_type: ty::t,
|
||||
_node_type: ty::t,
|
||||
cc: lib::llvm::CallConv,
|
||||
fn_ty: Type)
|
||||
-> ValueRef {
|
||||
|
@ -401,7 +401,9 @@ pub fn mark_for_expr(cx: &Context, e: &expr) {
|
||||
expr_match(*) | expr_block(_) | expr_if(*) | expr_while(*) |
|
||||
expr_break(_) | expr_again(_) | expr_unary(*) | expr_lit(_) |
|
||||
expr_mac(_) | expr_addr_of(*) | expr_ret(_) | expr_loop(*) |
|
||||
expr_loop_body(_) | expr_do_body(_) => ()
|
||||
expr_loop_body(_) | expr_do_body(_) => (),
|
||||
|
||||
expr_for_loop(*) => fail!("non-desugared expr_for_loop")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3240,6 +3240,8 @@ pub fn expr_kind(tcx: ctxt,
|
||||
RvalueStmtExpr
|
||||
}
|
||||
|
||||
ast::expr_for_loop(*) => fail!("non-desugared expr_for_loop"),
|
||||
|
||||
ast::expr_lit(_) | // Note: lit_str is carved out above
|
||||
ast::expr_unary(*) |
|
||||
ast::expr_addr_of(*) |
|
||||
|
@ -2559,6 +2559,8 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt,
|
||||
fcx.write_nil(id);
|
||||
}
|
||||
}
|
||||
ast::expr_for_loop(*) =>
|
||||
fail!("non-desugared expr_for_loop"),
|
||||
ast::expr_loop(ref body, _) => {
|
||||
check_block_no_value(fcx, (body));
|
||||
if !may_break(tcx, expr.id, body) {
|
||||
|
@ -1041,6 +1041,7 @@ pub mod guarantor {
|
||||
rcx.fcx.tcx(), rcx.fcx.inh.method_map, expr));
|
||||
None
|
||||
}
|
||||
ast::expr_for_loop(*) => fail!("non-desugared expr_for_loop"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -465,6 +465,7 @@ pub enum expr_ {
|
||||
expr_cast(@expr, Ty),
|
||||
expr_if(@expr, Block, Option<@expr>),
|
||||
expr_while(@expr, Block),
|
||||
expr_for_loop(@pat, @expr, Block),
|
||||
/* Conditionless loop (can be exited with break, cont, or ret)
|
||||
Same semantics as while(true) { body }, but typestate knows that the
|
||||
(implicit) condition is always true. */
|
||||
|
@ -16,11 +16,12 @@ use ast_util::{new_rename, new_mark, resolve};
|
||||
use attr;
|
||||
use attr::AttrMetaMethods;
|
||||
use codemap;
|
||||
use codemap::{span, ExpnInfo, NameAndSpan};
|
||||
use codemap::{span, spanned, ExpnInfo, NameAndSpan};
|
||||
use ext::base::*;
|
||||
use fold::*;
|
||||
use parse;
|
||||
use parse::{parse_item_from_source_str};
|
||||
use parse::token;
|
||||
use parse::token::{ident_to_str, intern};
|
||||
use visit;
|
||||
use visit::Visitor;
|
||||
@ -99,6 +100,159 @@ pub fn expand_expr(extsbox: @mut SyntaxEnv,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Desugar expr_for_loop
|
||||
// From: `foreach <src_pat> in <src_expr> <src_loop_block>`
|
||||
ast::expr_for_loop(src_pat, src_expr, ref src_loop_block) => {
|
||||
let src_pat = src_pat.clone();
|
||||
let src_expr = src_expr.clone();
|
||||
|
||||
// Expand any interior macros etc.
|
||||
// NB: we don't fold pats yet. Curious.
|
||||
let src_expr = fld.fold_expr(src_expr).clone();
|
||||
let src_loop_block = fld.fold_block(src_loop_block).clone();
|
||||
|
||||
let span = s;
|
||||
let lo = s.lo;
|
||||
let hi = s.hi;
|
||||
|
||||
pub fn mk_expr(cx: @ExtCtxt, span: span,
|
||||
node: expr_) -> @ast::expr {
|
||||
@ast::expr {
|
||||
id: cx.next_id(),
|
||||
node: node,
|
||||
span: span,
|
||||
}
|
||||
}
|
||||
|
||||
fn mk_block(cx: @ExtCtxt,
|
||||
stmts: &[@ast::stmt],
|
||||
expr: Option<@ast::expr>,
|
||||
span: span) -> ast::Block {
|
||||
ast::Block {
|
||||
view_items: ~[],
|
||||
stmts: stmts.to_owned(),
|
||||
expr: expr,
|
||||
id: cx.next_id(),
|
||||
rules: ast::DefaultBlock,
|
||||
span: span,
|
||||
}
|
||||
}
|
||||
|
||||
fn mk_simple_path(ident: ast::ident, span: span) -> ast::Path {
|
||||
ast::Path {
|
||||
span: span,
|
||||
global: false,
|
||||
idents: ~[ident],
|
||||
rp: None,
|
||||
types: ~[]
|
||||
}
|
||||
}
|
||||
|
||||
// to:
|
||||
//
|
||||
// {
|
||||
// let _i = &mut <src_expr>;
|
||||
// loop {
|
||||
// match i.next() {
|
||||
// None => break,
|
||||
// Some(<src_pat>) => <src_loop_block>
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
let local_ident = token::gensym_ident("i");
|
||||
let some_ident = token::str_to_ident("Some");
|
||||
let none_ident = token::str_to_ident("None");
|
||||
let next_ident = token::str_to_ident("next");
|
||||
|
||||
let local_path_1 = mk_simple_path(local_ident, span);
|
||||
let local_path_2 = mk_simple_path(local_ident, span);
|
||||
let some_path = mk_simple_path(some_ident, span);
|
||||
let none_path = mk_simple_path(none_ident, span);
|
||||
|
||||
// `let i = &mut <src_expr>`
|
||||
let iter_decl_stmt = {
|
||||
let ty = ast::Ty {
|
||||
id: cx.next_id(),
|
||||
node: ast::ty_infer,
|
||||
span: span
|
||||
};
|
||||
let local = @ast::Local {
|
||||
is_mutbl: false,
|
||||
ty: ty,
|
||||
pat: @ast::pat {
|
||||
id: cx.next_id(),
|
||||
node: ast::pat_ident(ast::bind_infer, local_path_1, None),
|
||||
span: src_expr.span
|
||||
},
|
||||
init: Some(mk_expr(cx, src_expr.span,
|
||||
ast::expr_addr_of(ast::m_mutbl, src_expr))),
|
||||
id: cx.next_id(),
|
||||
span: src_expr.span,
|
||||
};
|
||||
let e = @spanned(src_expr.span.lo,
|
||||
src_expr.span.hi,
|
||||
ast::decl_local(local));
|
||||
@spanned(lo, hi, ast::stmt_decl(e, cx.next_id()))
|
||||
};
|
||||
|
||||
// `None => break;`
|
||||
let none_arm = {
|
||||
let break_expr = mk_expr(cx, span, ast::expr_break(None));
|
||||
let break_stmt = @spanned(lo, hi, ast::stmt_expr(break_expr, cx.next_id()));
|
||||
let none_block = mk_block(cx, [break_stmt], None, span);
|
||||
let none_pat = @ast::pat {
|
||||
id: cx.next_id(),
|
||||
node: ast::pat_ident(ast::bind_infer, none_path, None),
|
||||
span: span
|
||||
};
|
||||
ast::arm {
|
||||
pats: ~[none_pat],
|
||||
guard: None,
|
||||
body: none_block
|
||||
}
|
||||
};
|
||||
|
||||
// `Some(<src_pat>) => <src_loop_block>`
|
||||
let some_arm = {
|
||||
let pat = @ast::pat {
|
||||
id: cx.next_id(),
|
||||
node: ast::pat_enum(some_path, Some(~[src_pat])),
|
||||
span: src_pat.span
|
||||
};
|
||||
ast::arm {
|
||||
pats: ~[pat],
|
||||
guard: None,
|
||||
body: src_loop_block
|
||||
}
|
||||
};
|
||||
|
||||
// `match i.next() { ... }`
|
||||
let match_stmt = {
|
||||
let local_expr = mk_expr(cx, span, ast::expr_path(local_path_2));
|
||||
let next_call_expr = mk_expr(cx, span,
|
||||
ast::expr_method_call(cx.next_id(),
|
||||
local_expr, next_ident,
|
||||
~[], ~[], ast::NoSugar));
|
||||
let match_expr = mk_expr(cx, span, ast::expr_match(next_call_expr,
|
||||
~[none_arm, some_arm]));
|
||||
@spanned(lo, hi, ast::stmt_expr(match_expr, cx.next_id()))
|
||||
};
|
||||
|
||||
// `loop { ... }`
|
||||
let loop_block = {
|
||||
let loop_body_block = mk_block(cx, [match_stmt], None, span);
|
||||
let loop_body_expr = mk_expr(cx, span, ast::expr_loop(loop_body_block, None));
|
||||
let loop_body_stmt = @spanned(lo, hi, ast::stmt_expr(loop_body_expr, cx.next_id()));
|
||||
mk_block(cx, [iter_decl_stmt,
|
||||
loop_body_stmt],
|
||||
None, span)
|
||||
};
|
||||
|
||||
(ast::expr_block(loop_block), span)
|
||||
}
|
||||
|
||||
_ => orig(e, s, fld)
|
||||
}
|
||||
}
|
||||
|
@ -559,6 +559,11 @@ pub fn noop_fold_expr(e: &expr_, fld: @ast_fold) -> expr_ {
|
||||
expr_while(cond, ref body) => {
|
||||
expr_while(fld.fold_expr(cond), fld.fold_block(body))
|
||||
}
|
||||
expr_for_loop(pat, iter, ref body) => {
|
||||
expr_for_loop(fld.fold_pat(pat),
|
||||
fld.fold_expr(iter),
|
||||
fld.fold_block(body))
|
||||
}
|
||||
expr_loop(ref body, opt_ident) => {
|
||||
expr_loop(
|
||||
fld.fold_block(body),
|
||||
|
@ -28,6 +28,7 @@ pub fn expr_requires_semi_to_be_stmt(e: @ast::expr) -> bool {
|
||||
| ast::expr_block(_)
|
||||
| ast::expr_while(*)
|
||||
| ast::expr_loop(*)
|
||||
| ast::expr_for_loop(*)
|
||||
| ast::expr_call(_, _, ast::DoSugar)
|
||||
| ast::expr_call(_, _, ast::ForSugar)
|
||||
| ast::expr_method_call(_, _, _, _, _, ast::DoSugar)
|
||||
|
@ -29,7 +29,7 @@ use ast::{expr_method_call, expr_paren, expr_path, expr_repeat};
|
||||
use ast::{expr_ret, expr_self, expr_struct, expr_tup, expr_unary};
|
||||
use ast::{expr_vec, expr_vstore, expr_vstore_mut_box};
|
||||
use ast::{expr_vstore_slice, expr_vstore_box};
|
||||
use ast::{expr_vstore_mut_slice, expr_while, extern_fn, Field, fn_decl};
|
||||
use ast::{expr_vstore_mut_slice, expr_while, expr_for_loop, extern_fn, Field, fn_decl};
|
||||
use ast::{expr_vstore_uniq, Onceness, Once, Many};
|
||||
use ast::{foreign_item, foreign_item_static, foreign_item_fn, foreign_mod};
|
||||
use ast::{ident, impure_fn, inherited, item, item_, item_static};
|
||||
@ -1622,6 +1622,8 @@ impl Parser {
|
||||
hi = self.span.hi;
|
||||
} else if self.eat_keyword(keywords::If) {
|
||||
return self.parse_if_expr();
|
||||
} else if self.eat_keyword(keywords::ForEach) {
|
||||
return self.parse_for_expr();
|
||||
} else if self.eat_keyword(keywords::For) {
|
||||
return self.parse_sugary_call_expr(lo, ~"for", ForSugar,
|
||||
expr_loop_body);
|
||||
@ -2323,6 +2325,21 @@ impl Parser {
|
||||
}
|
||||
}
|
||||
|
||||
// parse a 'foreach' .. 'in' expression ('foreach' token already eaten)
|
||||
pub fn parse_for_expr(&self) -> @expr {
|
||||
// Parse: `foreach <src_pat> in <src_expr> <src_loop_block>`
|
||||
|
||||
let lo = self.last_span.lo;
|
||||
let pat = self.parse_pat();
|
||||
self.expect_keyword(keywords::In);
|
||||
let expr = self.parse_expr();
|
||||
let loop_block = self.parse_block();
|
||||
let hi = self.span.hi;
|
||||
|
||||
self.mk_expr(lo, hi, expr_for_loop(pat, expr, loop_block))
|
||||
}
|
||||
|
||||
|
||||
// parse a 'for' or 'do'.
|
||||
// the 'for' and 'do' expressions parse as calls, but look like
|
||||
// function calls followed by a closure expression.
|
||||
|
@ -474,6 +474,8 @@ fn mk_fresh_ident_interner() -> @ident_interner {
|
||||
"while", // 64
|
||||
|
||||
"be", // 65
|
||||
"in", // 66
|
||||
"foreach", // 67
|
||||
];
|
||||
|
||||
@ident_interner {
|
||||
@ -570,8 +572,10 @@ pub mod keywords {
|
||||
False,
|
||||
Fn,
|
||||
For,
|
||||
ForEach,
|
||||
If,
|
||||
Impl,
|
||||
In,
|
||||
Let,
|
||||
__Log,
|
||||
Loop,
|
||||
@ -612,8 +616,10 @@ pub mod keywords {
|
||||
False => ident { name: 40, ctxt: 0 },
|
||||
Fn => ident { name: 41, ctxt: 0 },
|
||||
For => ident { name: 42, ctxt: 0 },
|
||||
ForEach => ident { name: 67, ctxt: 0 },
|
||||
If => ident { name: 43, ctxt: 0 },
|
||||
Impl => ident { name: 44, ctxt: 0 },
|
||||
In => ident { name: 66, ctxt: 0 },
|
||||
Let => ident { name: 45, ctxt: 0 },
|
||||
__Log => ident { name: 46, ctxt: 0 },
|
||||
Loop => ident { name: 47, ctxt: 0 },
|
||||
|
@ -1228,6 +1228,15 @@ pub fn print_expr(s: @ps, expr: &ast::expr) {
|
||||
space(s.s);
|
||||
print_block(s, blk);
|
||||
}
|
||||
ast::expr_for_loop(pat, iter, ref blk) => {
|
||||
head(s, "foreach");
|
||||
print_pat(s, pat);
|
||||
space(s.s);
|
||||
word_space(s, "in");
|
||||
print_expr(s, iter);
|
||||
space(s.s);
|
||||
print_block(s, blk);
|
||||
}
|
||||
ast::expr_loop(ref blk, opt_ident) => {
|
||||
for opt_ident.iter().advance |ident| {
|
||||
word(s.s, "'");
|
||||
|
@ -512,6 +512,11 @@ pub fn visit_expr<E:Clone>(ex: @expr, (e, v): (E, vt<E>)) {
|
||||
(v.visit_expr)(x, (e.clone(), v));
|
||||
(v.visit_block)(b, (e.clone(), v));
|
||||
}
|
||||
expr_for_loop(pat, iter, ref b) => {
|
||||
(v.visit_pat)(pat, (e.clone(), v));
|
||||
(v.visit_expr)(iter, (e.clone(), v));
|
||||
(v.visit_block)(b, (e.clone(), v));
|
||||
}
|
||||
expr_loop(ref b, _) => (v.visit_block)(b, (e.clone(), v)),
|
||||
expr_match(x, ref arms) => {
|
||||
(v.visit_expr)(x, (e.clone(), v));
|
||||
|
21
src/test/run-pass/foreach-external-iterators-break.rs
Normal file
21
src/test/run-pass/foreach-external-iterators-break.rs
Normal file
@ -0,0 +1,21 @@
|
||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
fn main() {
|
||||
let x = [1,..100];
|
||||
let mut y = 0;
|
||||
foreach i in x.iter() {
|
||||
if y > 10 {
|
||||
break;
|
||||
}
|
||||
y += *i;
|
||||
}
|
||||
assert!(y == 11);
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use std::hashmap::HashMap;
|
||||
|
||||
// This is a fancy one: it uses an external iterator established
|
||||
// outside the loop, breaks, then _picks back up_ and continues
|
||||
// iterating with it.
|
||||
|
||||
fn main() {
|
||||
let mut h = HashMap::new();
|
||||
let kvs = [(1, 10), (2, 20), (3, 30)];
|
||||
foreach &(k,v) in kvs.iter() {
|
||||
h.insert(k,v);
|
||||
}
|
||||
let mut x = 0;
|
||||
let mut y = 0;
|
||||
|
||||
let mut i = h.iter();
|
||||
|
||||
foreach (&k,&v) in i {
|
||||
x += k;
|
||||
y += v;
|
||||
break;
|
||||
}
|
||||
|
||||
foreach (&k,&v) in i {
|
||||
x += k;
|
||||
y += v;
|
||||
}
|
||||
|
||||
assert_eq!(x, 6);
|
||||
assert_eq!(y, 60);
|
||||
}
|
27
src/test/run-pass/foreach-external-iterators-hashmap.rs
Normal file
27
src/test/run-pass/foreach-external-iterators-hashmap.rs
Normal file
@ -0,0 +1,27 @@
|
||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use std::hashmap::HashMap;
|
||||
|
||||
fn main() {
|
||||
let mut h = HashMap::new();
|
||||
let kvs = [(1, 10), (2, 20), (3, 30)];
|
||||
foreach &(k,v) in kvs.iter() {
|
||||
h.insert(k,v);
|
||||
}
|
||||
let mut x = 0;
|
||||
let mut y = 0;
|
||||
foreach (&k,&v) in h.iter() {
|
||||
x += k;
|
||||
y += v;
|
||||
}
|
||||
assert_eq!(x, 6);
|
||||
assert_eq!(y, 60);
|
||||
}
|
21
src/test/run-pass/foreach-external-iterators-loop.rs
Normal file
21
src/test/run-pass/foreach-external-iterators-loop.rs
Normal file
@ -0,0 +1,21 @@
|
||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
fn main() {
|
||||
let x = [1,..100];
|
||||
let mut y = 0;
|
||||
foreach (n,i) in x.iter().enumerate() {
|
||||
if n < 10 {
|
||||
loop;
|
||||
}
|
||||
y += *i;
|
||||
}
|
||||
assert_eq!(y, 90);
|
||||
}
|
23
src/test/run-pass/foreach-external-iterators-nested.rs
Normal file
23
src/test/run-pass/foreach-external-iterators-nested.rs
Normal file
@ -0,0 +1,23 @@
|
||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
fn main() {
|
||||
let x = [1,..100];
|
||||
let y = [2,..100];
|
||||
let mut p = 0;
|
||||
let mut q = 0;
|
||||
foreach i in x.iter() {
|
||||
foreach j in y.iter() {
|
||||
p += *j;
|
||||
}
|
||||
q += *i + p;
|
||||
}
|
||||
assert!(q == 1010100);
|
||||
}
|
18
src/test/run-pass/foreach-external-iterators.rs
Normal file
18
src/test/run-pass/foreach-external-iterators.rs
Normal file
@ -0,0 +1,18 @@
|
||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
fn main() {
|
||||
let x = [1,..100];
|
||||
let mut y = 0;
|
||||
foreach i in x.iter() {
|
||||
y += *i
|
||||
}
|
||||
assert!(y == 100);
|
||||
}
|
Loading…
Reference in New Issue
Block a user