From 96229e82985054a7b1f95732605eeeda69bf71b9 Mon Sep 17 00:00:00 2001 From: llogiq Date: Fri, 8 May 2015 13:26:45 +0200 Subject: [PATCH 01/20] fixed ICE issue #25180 --- src/librustc/middle/check_const.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/librustc/middle/check_const.rs b/src/librustc/middle/check_const.rs index 8bb83c54da8a3..195f4f4b69568 100644 --- a/src/librustc/middle/check_const.rs +++ b/src/librustc/middle/check_const.rs @@ -251,9 +251,13 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> { b: &'v ast::Block, s: Span, fn_id: ast::NodeId) { - assert!(self.mode == Mode::Var); - self.with_euv(Some(fn_id), |euv| euv.walk_fn(fd, b)); - visit::walk_fn(self, fk, fd, b, s); + let (old_mode, old_qualif) = (self.mode, self.qualif); + self.mode = Mode::Var; + self.qualif = ConstQualif::empty(); + self.with_euv(Some(fn_id), |euv| euv.walk_fn(fd, b)); + visit::walk_fn(self, fk, fd, b, s); + self.mode = old_mode; + self.qualif = old_qualif; } fn visit_pat(&mut self, p: &ast::Pat) { @@ -275,7 +279,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> { let node_ty = ty::node_id_to_type(self.tcx, ex.id); check_expr(self, ex, node_ty); - + // Special-case some expressions to avoid certain flags bubbling up. match ex.node { ast::ExprCall(ref callee, ref args) => { From 29daa29981e89ba7f71dcbba06166f8c54f4682d Mon Sep 17 00:00:00 2001 From: llogiq Date: Fri, 8 May 2015 13:56:14 +0200 Subject: [PATCH 02/20] formatting correction --- src/librustc/middle/check_const.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/librustc/middle/check_const.rs b/src/librustc/middle/check_const.rs index 195f4f4b69568..2322b97de1487 100644 --- a/src/librustc/middle/check_const.rs +++ b/src/librustc/middle/check_const.rs @@ -254,10 +254,10 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> { let (old_mode, old_qualif) = (self.mode, self.qualif); self.mode = Mode::Var; self.qualif = ConstQualif::empty(); - self.with_euv(Some(fn_id), |euv| euv.walk_fn(fd, b)); - visit::walk_fn(self, fk, fd, b, s); - self.mode = old_mode; - self.qualif = old_qualif; + self.with_euv(Some(fn_id), |euv| euv.walk_fn(fd, b)); + visit::walk_fn(self, fk, fd, b, s); + self.mode = old_mode; + self.qualif = old_qualif; } fn visit_pat(&mut self, p: &ast::Pat) { @@ -279,7 +279,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> { let node_ty = ty::node_id_to_type(self.tcx, ex.id); check_expr(self, ex, node_ty); - + // Special-case some expressions to avoid certain flags bubbling up. match ex.node { ast::ExprCall(ref callee, ref args) => { From 1434715550c372f83ab3a138d66674d5b303d429 Mon Sep 17 00:00:00 2001 From: llogiq Date: Fri, 8 May 2015 17:07:03 +0200 Subject: [PATCH 03/20] crossed the i-s and dotted the t-s ;-) Also added a run-pass test and removed the with_mode copy, thanks to eddyb --- src/librustc/middle/check_const.rs | 8 ++------ src/test/run-pass/issue-25180.rs | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 6 deletions(-) create mode 100644 src/test/run-pass/issue-25180.rs diff --git a/src/librustc/middle/check_const.rs b/src/librustc/middle/check_const.rs index 2322b97de1487..d48d2abc660f6 100644 --- a/src/librustc/middle/check_const.rs +++ b/src/librustc/middle/check_const.rs @@ -251,13 +251,9 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> { b: &'v ast::Block, s: Span, fn_id: ast::NodeId) { - let (old_mode, old_qualif) = (self.mode, self.qualif); - self.mode = Mode::Var; - self.qualif = ConstQualif::empty(); - self.with_euv(Some(fn_id), |euv| euv.walk_fn(fd, b)); + self.with_mode(Mode::Var, |v| v.with_euv(Some(fn_id), + |euv| euv.walk_fn(fd, b))); visit::walk_fn(self, fk, fd, b, s); - self.mode = old_mode; - self.qualif = old_qualif; } fn visit_pat(&mut self, p: &ast::Pat) { diff --git a/src/test/run-pass/issue-25180.rs b/src/test/run-pass/issue-25180.rs new file mode 100644 index 0000000000000..3955e451639fd --- /dev/null +++ b/src/test/run-pass/issue-25180.rs @@ -0,0 +1,16 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// pretty-expanded FIXME #25180 + +const x: &'static Fn() = &|| println!("ICE here"); + +fn main() {} + From 101b25c10031b0230674f86712ce709e5e6f6023 Mon Sep 17 00:00:00 2001 From: Clark Gaebel Date: Sat, 7 Feb 2015 18:49:54 -0500 Subject: [PATCH 04/20] [liballoc] Adds checks for UB during allocation. They're only enabled in debug builds, but a panic is usually more welcome than UB in debug builds. --- src/liballoc/heap.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/liballoc/heap.rs b/src/liballoc/heap.rs index 86a04a0687a5b..b41ffcda3a5f3 100644 --- a/src/liballoc/heap.rs +++ b/src/liballoc/heap.rs @@ -8,6 +8,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use core::{isize, usize}; + +#[inline(always)] +fn check_size_and_alignment(size: usize, align: usize) { + debug_assert!(size != 0); + debug_assert!(size <= isize::MAX as usize, "Tried to allocate too much: {} bytes", size); + debug_assert!(usize::is_power_of_two(align), "Invalid alignment of allocation: {}", align); +} + // FIXME: #13996: mark the `allocate` and `reallocate` return value as `noalias` /// Return a pointer to `size` bytes of memory aligned to `align`. @@ -19,6 +28,7 @@ /// size on the platform. #[inline] pub unsafe fn allocate(size: usize, align: usize) -> *mut u8 { + check_size_and_alignment(size, align); imp::allocate(size, align) } @@ -38,6 +48,7 @@ pub unsafe fn allocate(size: usize, align: usize) -> *mut u8 { /// any value in range_inclusive(requested_size, usable_size). #[inline] pub unsafe fn reallocate(ptr: *mut u8, old_size: usize, size: usize, align: usize) -> *mut u8 { + check_size_and_alignment(size, align); imp::reallocate(ptr, old_size, size, align) } @@ -56,6 +67,7 @@ pub unsafe fn reallocate(ptr: *mut u8, old_size: usize, size: usize, align: usiz #[inline] pub unsafe fn reallocate_inplace(ptr: *mut u8, old_size: usize, size: usize, align: usize) -> usize { + check_size_and_alignment(size, align); imp::reallocate_inplace(ptr, old_size, size, align) } From ca5c694f439fdb86bd7314b7daaa56994e33c761 Mon Sep 17 00:00:00 2001 From: llogiq Date: Sun, 10 May 2015 04:39:51 +0200 Subject: [PATCH 05/20] Pulled walk_fn() into Var mode, extended test --- src/librustc/middle/check_const.rs | 7 ++++--- src/test/run-pass/issue-25180.rs | 4 +++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/librustc/middle/check_const.rs b/src/librustc/middle/check_const.rs index d48d2abc660f6..89a93f990df63 100644 --- a/src/librustc/middle/check_const.rs +++ b/src/librustc/middle/check_const.rs @@ -251,9 +251,10 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> { b: &'v ast::Block, s: Span, fn_id: ast::NodeId) { - self.with_mode(Mode::Var, |v| v.with_euv(Some(fn_id), - |euv| euv.walk_fn(fd, b))); - visit::walk_fn(self, fk, fd, b, s); + self.with_mode(Mode::Var, |v| { + v.with_euv(Some(fn_id), |euv| euv.walk_fn(fd, b)); + visit::walk_fn(v, fk, fd, b, s); + }) } fn visit_pat(&mut self, p: &ast::Pat) { diff --git a/src/test/run-pass/issue-25180.rs b/src/test/run-pass/issue-25180.rs index 3955e451639fd..d63c50192426b 100644 --- a/src/test/run-pass/issue-25180.rs +++ b/src/test/run-pass/issue-25180.rs @@ -12,5 +12,7 @@ const x: &'static Fn() = &|| println!("ICE here"); -fn main() {} +fn main() { + x() +} From 6e19cfacf42e2de001de4c2133a4321b5286f523 Mon Sep 17 00:00:00 2001 From: llogiq Date: Sun, 10 May 2015 11:57:52 +0200 Subject: [PATCH 06/20] more tests --- src/test/run-pass/issue-25180.rs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/test/run-pass/issue-25180.rs b/src/test/run-pass/issue-25180.rs index d63c50192426b..0ad5fd87e9566 100644 --- a/src/test/run-pass/issue-25180.rs +++ b/src/test/run-pass/issue-25180.rs @@ -10,9 +10,21 @@ // pretty-expanded FIXME #25180 -const x: &'static Fn() = &|| println!("ICE here"); +const empty: &'static Fn() = &|| println!("ICE here"); -fn main() { - x() +const one_argument: &'static Fn(u32) = &|y| println("{}", y); + +const plus_21: &'static Fn(u32) -> u32 = |y| y + 21; + +const multi_and_local: &'static Fn(u32, u32) -> u32 = |x, y| { + let tmp = x + y; + tmp * 2; +}; + +pub fn main() { + empty(); + one_argument(42); + assert!(plus_21(21) == 42); + assert!(multi_and_local(1, 2) == 6); } From a18ce4d47979260001e9be0a92f887e3abd4eb88 Mon Sep 17 00:00:00 2001 From: llogiq Date: Sun, 10 May 2015 12:30:06 +0200 Subject: [PATCH 07/20] fixed errors in test code --- src/test/run-pass/issue-25180.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/test/run-pass/issue-25180.rs b/src/test/run-pass/issue-25180.rs index 0ad5fd87e9566..9d2d51264e6fa 100644 --- a/src/test/run-pass/issue-25180.rs +++ b/src/test/run-pass/issue-25180.rs @@ -10,21 +10,21 @@ // pretty-expanded FIXME #25180 -const empty: &'static Fn() = &|| println!("ICE here"); +const EMPTY: &'static Fn() = &|| println!("ICE here"); -const one_argument: &'static Fn(u32) = &|y| println("{}", y); +const ONE_ARGUMENT: &'static Fn(u32) = &|y| println!("{}", y); -const plus_21: &'static Fn(u32) -> u32 = |y| y + 21; +const PLUS_21: &'static (Fn(u32) -> u32) = &|y| y + 21; -const multi_and_local: &'static Fn(u32, u32) -> u32 = |x, y| { +const MULTI_AND_LOCAL: &'static (Fn(u32, u32) -> u32) = &|x, y| { let tmp = x + y; - tmp * 2; + tmp * 2 }; pub fn main() { - empty(); - one_argument(42); - assert!(plus_21(21) == 42); - assert!(multi_and_local(1, 2) == 6); + EMPTY(); + ONE_ARGUMENT(42); + assert!(PLUS_21(21) == 42); + assert!(MULTI_AND_LOCAL(1, 2) == 6); } From 1276506f6832ad0376e0ecbfc8b82f80c4293b91 Mon Sep 17 00:00:00 2001 From: Cole Reynolds Date: Sun, 10 May 2015 15:42:29 -0400 Subject: [PATCH 08/20] Fix grammar in path::Path documentation Change from "must be used with behind a pointer" to "must be used behind a pointer" --- src/libstd/path.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstd/path.rs b/src/libstd/path.rs index 8ccc387c90277..c9c867615d225 100644 --- a/src/libstd/path.rs +++ b/src/libstd/path.rs @@ -1199,7 +1199,7 @@ impl Into for PathBuf { /// absolute, and so on. More details about the overall approach can be found in /// the module documentation. /// -/// This is an *unsized* type, meaning that it must always be used with behind a +/// This is an *unsized* type, meaning that it must always be used behind a /// pointer like `&` or `Box`. /// /// # Examples From f11c4ba30c09016b07e763414c7295416283d853 Mon Sep 17 00:00:00 2001 From: Jan-Erik Rediger Date: Sun, 10 May 2015 23:12:45 +0200 Subject: [PATCH 09/20] Remove extra parentheses --- src/doc/reference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/reference.md b/src/doc/reference.md index 16fdcfa301392..29290acf6dc51 100644 --- a/src/doc/reference.md +++ b/src/doc/reference.md @@ -547,7 +547,7 @@ _name_ s that occur in its body. At the "current layer", they all must repeat the same number of times, so ` ( $( $i:ident ),* ; $( $j:ident ),* ) => ( $( ($i,$j) ),* )` is valid if given the argument `(a,b,c ; d,e,f)`, but not `(a,b,c ; d,e)`. The repetition walks through the choices at that layer in -lockstep, so the former input transcribes to `( (a,d), (b,e), (c,f) )`. +lockstep, so the former input transcribes to `(a,d), (b,e), (c,f)`. Nested repetitions are allowed. From a6dc983a604ec3a0e89a08419c57c256ba1a0e4a Mon Sep 17 00:00:00 2001 From: Nick Hamann Date: Sat, 9 May 2015 20:38:10 -0500 Subject: [PATCH 10/20] Add some comments for error codes in librustc_typeck/diagnostics --- src/librustc_typeck/diagnostics.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 026ba3d08b42b..904cd156f2a29 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -164,18 +164,18 @@ register_diagnostics! { E0030, E0031, E0033, - E0034, - E0035, - E0036, - E0038, + E0034, // multiple applicable methods in scope + E0035, // does not take type parameters + E0036, // incorrect number of type parameters given for this method + E0038, // cannot convert to a trait object because trait is not object-safe E0040, // explicit use of destructor method - E0044, - E0045, + E0044, // foreign items may not have type parameters + E0045, // variadic function must have C calling convention E0049, E0050, E0053, - E0055, - E0057, + E0055, // method has an incompatible type for trait + E0057, // method has an incompatible type for trait E0059, E0060, E0061, From d730750d24e74263e349b0d5f32a8922065829c0 Mon Sep 17 00:00:00 2001 From: Nick Hamann Date: Sun, 10 May 2015 03:48:15 -0500 Subject: [PATCH 11/20] Add long diagnostics for E0184, E0204, E0205, E0206, E0243, E0244. --- src/librustc_typeck/diagnostics.rs | 117 ++++++++++++++++++++++++++--- 1 file changed, 108 insertions(+), 9 deletions(-) diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 904cd156f2a29..7428e5ada5f20 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -150,6 +150,114 @@ attribute. Such a function must have the following type signature: ``` fn(isize, *const *const u8) -> isize ``` +"##, + +E0184: r##" +Explicitly implementing both Drop and Copy for a type is currently disallowed. +This feature can make some sense in theory, but the current implementation is +incorrect and can lead to memory unsafety (see issue #20126), so it has been +disabled for now. +"##, + +E0204: r##" +An attempt to implement the `Copy` trait for a struct failed because one of the +fields does not implement `Copy`. To fix this, you must implement `Copy` for the +mentioned field. Note that this may not be possible, as in the example of + +``` +struct Foo { + foo : Vec, +} + +impl Copy for Foo { } +``` + +This fails because `Vec` does not implement `Copy` for any `T`. + +Here's another example that will fail: + +``` +#[derive(Copy)] +struct Foo<'a> { + ty: &'a mut bool, +} +``` + +This fails because `&mut T` is not `Copy`, even when `T` is `Copy` (as opposed +to `&T`, which is). +"##, + +E0205: r##" +An attempt to implement the `Copy` trait for an enum failed because one of the +variants does not implement `Copy`. To fix this, you must implement `Copy` for +the mentioned variant. Note that this may not be possible, as in the example of + +``` +enum Foo { + Bar(Vec), + Baz, +} + +impl Copy for Foo { } +``` + +This fails because `Vec` does not implement `Copy` for any `T`. + +Here's another example that will fail: + +``` +#[derive(Copy)] +enum Foo<'a> { + Bar(&'a mut bool), + Baz +} +``` + +This fails because `&mut T` is not `Copy`, even when `T` is `Copy` (as opposed +to `&T`, which is). +"##, + +E0206: r##" +You can only implement `Copy` for a struct or enum. For example, both of the +following examples will fail, because neither `i32` nor `&'static mut Bar` is +a struct or enum: + +``` +type Foo = i32; +impl Copy for Foo { } // error + +#[derive(Copy, Clone)] +struct Bar; +impl Copy for &'static mut Bar { } // error +``` +"##, + +E0243: r##" +This error indicates that not enough type parameters were found in a type or +trait. + +For example, the `Foo` struct below is defined to be generic in `T`, but the +type parameter is missing in the definition of `Bar`: + +``` +struct Foo { x: T } + +struct Bar { x: Foo } +``` +"##, + +E0244: r##" +This error indicates that too many type parameters were found in a type or +trait. + +For example, the `Foo` struct below has no type parameters, but is supplied +with two in the definition of `Bar`: + +``` +struct Foo { x: bool } + +struct Bar { x: Foo } +``` "## } @@ -232,7 +340,6 @@ register_diagnostics! { E0178, E0182, E0183, - E0184, E0185, E0186, E0187, // can't infer the kind of the closure @@ -254,12 +361,6 @@ register_diagnostics! { E0202, // associated items are not allowed in inherent impls E0203, // type parameter has more than one relaxed default bound, // and only one is supported - E0204, // trait `Copy` may not be implemented for this type; field - // does not implement `Copy` - E0205, // trait `Copy` may not be implemented for this type; variant - // does not implement `copy` - E0206, // trait `Copy` may not be implemented for this type; type is - // not a structure or enumeration E0207, // type parameter is not constrained by the impl trait, self type, or predicate E0208, E0209, // builtin traits can only be implemented on structs or enums @@ -296,8 +397,6 @@ register_diagnostics! { E0240, E0241, E0242, // internal error looking up a definition - E0243, // wrong number of type arguments - E0244, // wrong number of type arguments E0245, // not a trait E0246, // illegal recursive type E0247, // found module name used as a type From a40c49c6dba55c8074a79fa26fbf2bc0d9387371 Mon Sep 17 00:00:00 2001 From: Nick Hamann Date: Sun, 10 May 2015 12:55:12 -0500 Subject: [PATCH 12/20] Improve wording for E0204 and E0205 long diagnostic messages. --- src/librustc_typeck/diagnostics.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 7428e5ada5f20..887e15bd6b615 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -183,8 +183,8 @@ struct Foo<'a> { } ``` -This fails because `&mut T` is not `Copy`, even when `T` is `Copy` (as opposed -to `&T`, which is). +This fails because `&mut T` is not `Copy`, even when `T` is `Copy` (this +differs from the behavior for `&T`, which is `Copy` when `T` is `Copy`). "##, E0205: r##" @@ -213,8 +213,8 @@ enum Foo<'a> { } ``` -This fails because `&mut T` is not `Copy`, even when `T` is `Copy` (as opposed -to `&T`, which is). +This fails because `&mut T` is not `Copy`, even when `T` is `Copy` (this +differs from the behavior for `&T`, which is `Copy` when `T` is `Copy`). "##, E0206: r##" From 021b3f55d6a10e668a6fa7b51b4f8cd10a2493e5 Mon Sep 17 00:00:00 2001 From: Nick Hamann Date: Sun, 10 May 2015 13:01:40 -0500 Subject: [PATCH 13/20] Revise the E0206 long diagnostics message. --- src/librustc_typeck/diagnostics.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 887e15bd6b615..89b7e8c5747fa 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -218,9 +218,9 @@ differs from the behavior for `&T`, which is `Copy` when `T` is `Copy`). "##, E0206: r##" -You can only implement `Copy` for a struct or enum. For example, both of the -following examples will fail, because neither `i32` nor `&'static mut Bar` is -a struct or enum: +You can only implement `Copy` for a struct or enum. Both of the following +examples will fail, because neither `i32` (primitive type) nor `&'static Bar` +(reference to `Bar`) is a struct or enum: ``` type Foo = i32; @@ -228,7 +228,7 @@ impl Copy for Foo { } // error #[derive(Copy, Clone)] struct Bar; -impl Copy for &'static mut Bar { } // error +impl Copy for &'static Bar { } // error ``` "##, From ed1bc684fc28d26635eed49a8a39e6c492782564 Mon Sep 17 00:00:00 2001 From: Nick Hamann Date: Sun, 10 May 2015 13:17:12 -0500 Subject: [PATCH 14/20] Add a link to the E0184 long diagnostic message. --- src/librustc_typeck/diagnostics.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 89b7e8c5747fa..373be62073648 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -155,8 +155,10 @@ fn(isize, *const *const u8) -> isize E0184: r##" Explicitly implementing both Drop and Copy for a type is currently disallowed. This feature can make some sense in theory, but the current implementation is -incorrect and can lead to memory unsafety (see issue #20126), so it has been -disabled for now. +incorrect and can lead to memory unsafety (see [issue #20126][iss20126]), so +it has been disabled for now. + +[iss20126]: https://github.com/rust-lang/rust/issues/20126 "##, E0204: r##" From c98df58593254374dac0ab941fe753e4fd6938e3 Mon Sep 17 00:00:00 2001 From: Paul Quint Date: Sun, 10 May 2015 18:08:44 -0500 Subject: [PATCH 15/20] Update BitSet docs to correct types Update BitSet docs to correct type in one more spot removed accidental file --- src/libcollections/bit.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libcollections/bit.rs b/src/libcollections/bit.rs index 17e937c5af257..fd53f9eee9305 100644 --- a/src/libcollections/bit.rs +++ b/src/libcollections/bit.rs @@ -1537,7 +1537,7 @@ impl BitSet { bit_vec.nbits = trunc_len * u32::BITS; } - /// Iterator over each u32 stored in the `BitSet`. + /// Iterator over each usize stored in the `BitSet`. /// /// # Examples /// @@ -1558,7 +1558,7 @@ impl BitSet { SetIter {set: self, next_idx: 0} } - /// Iterator over each u32 stored in `self` union `other`. + /// Iterator over each usize stored in `self` union `other`. /// See [union_with](#method.union_with) for an efficient in-place version. /// /// # Examples @@ -1658,7 +1658,7 @@ impl BitSet { }) } - /// Iterator over each u32 stored in the symmetric difference of `self` and `other`. + /// Iterator over each usize stored in the symmetric difference of `self` and `other`. /// See [symmetric_difference_with](#method.symmetric_difference_with) for /// an efficient in-place version. /// From 3c4facbcfe16ea0919e7a8537cae219c14a2b1ca Mon Sep 17 00:00:00 2001 From: Nick Hamann Date: Sun, 10 May 2015 18:58:21 -0500 Subject: [PATCH 16/20] Add long diagnostics for E0249 and E0250 --- src/librustc_typeck/astconv.rs | 3 ++- src/librustc_typeck/diagnostics.rs | 34 ++++++++++++++++++++++++++++-- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 677254238c034..54ec1aace9211 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -1603,7 +1603,8 @@ pub fn ast_ty_to_ty<'tcx>(this: &AstConv<'tcx>, Some(i as usize)), _ => { span_err!(tcx.sess, ast_ty.span, E0249, - "expected constant expr for array length"); + "expected constant integer expression \ + for array length"); this.tcx().types.err } } diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 373be62073648..ea872d1014425 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -260,6 +260,38 @@ struct Foo { x: bool } struct Bar { x: Foo } ``` +"##, + +E0249: r##" +This error indicates a constant expression for the array length was found, but +it was not an integer (signed or unsigned) expression. + +Some examples of code that produces this error are: + +``` +const A: [u32; "hello"] = []; // error +const B: [u32; true] = []; // error +const C: [u32; 0.0] = []; // error +"##, + +E0250: r##" +This means there was an error while evaluating the expression for the length of +a fixed-size array type. + +Some examples of code that produces this error are: + +``` +// divide by zero in the length expression +const A: [u32; 1/0] = []; + +// Rust currently will not evaluate the function `foo` at compile time +fn foo() -> usize { 12 } +const B: [u32; foo()] = []; + +// it is an error to try to add `u8` and `f64` +use std::{f64, u8}; +const C: [u32; u8::MAX + f64::EPSILON] = []; +``` "## } @@ -403,8 +435,6 @@ register_diagnostics! { E0246, // illegal recursive type E0247, // found module name used as a type E0248, // found value name used as a type - E0249, // expected constant expr for array length - E0250, // expected constant expr for array length E0318, // can't create default impls for traits outside their crates E0319, // trait impls for defaulted traits allowed just for structs/enums E0320, // recursive overflow during dropck From ce7ef32982541727cdbeda1960364cb09e7ca63c Mon Sep 17 00:00:00 2001 From: Michael Sproul Date: Mon, 11 May 2015 23:04:31 +1000 Subject: [PATCH 17/20] Markdown formatting for error explanations. --- src/librustc/diagnostics.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index aaf615ee40409..0f1e55544e1af 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -745,6 +745,7 @@ variable. For example: +``` let x: i32 = "I am not a number!"; // ~~~ ~~~~~~~~~~~~~~~~~~~~ // | | @@ -752,6 +753,7 @@ let x: i32 = "I am not a number!"; // | compiler infers type `&str` // | // type `i32` assigned to variable `x` +``` "##, E0309: r##" @@ -760,6 +762,7 @@ how long the data stored within them is guaranteed to be live. This lifetime must be as long as the data needs to be alive, and missing the constraint that denotes this will cause this error. +``` // This won't compile because T is not constrained, meaning the data // stored in it is not guaranteed to last as long as the reference struct Foo<'a, T> { @@ -770,6 +773,7 @@ struct Foo<'a, T> { struct Foo<'a, T: 'a> { foo: &'a T } +``` "##, E0310: r##" @@ -778,6 +782,7 @@ how long the data stored within them is guaranteed to be live. This lifetime must be as long as the data needs to be alive, and missing the constraint that denotes this will cause this error. +``` // This won't compile because T is not constrained to the static lifetime // the reference needs struct Foo { @@ -788,6 +793,7 @@ struct Foo { struct Foo { foo: &'static T } +``` "## } From 7dd7bea3ac8ebaa6059e885c24d1388d2c7d03ac Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 11 May 2015 16:46:24 +0200 Subject: [PATCH 18/20] trpl: item macros must be followed by a semicolon --- src/doc/trpl/compiler-plugins.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/trpl/compiler-plugins.md b/src/doc/trpl/compiler-plugins.md index 9eb22a7f6985a..127e097c34f7d 100644 --- a/src/doc/trpl/compiler-plugins.md +++ b/src/doc/trpl/compiler-plugins.md @@ -176,7 +176,7 @@ for a full example, the core of which is reproduced here: ```ignore declare_lint!(TEST_LINT, Warn, - "Warn about items named 'lintme'") + "Warn about items named 'lintme'"); struct Pass; From dd7d0f5e9cd3e76f9781bf1a2f0a45b02aabe9d6 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Sun, 10 May 2015 18:11:51 -0400 Subject: [PATCH 19/20] Fix links in the referece and remove references to the unit type, which no longer exists Fixes #24999 --- src/doc/reference.md | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/src/doc/reference.md b/src/doc/reference.md index 66a7f543ad95f..10469bae8aa1e 100644 --- a/src/doc/reference.md +++ b/src/doc/reference.md @@ -31,23 +31,27 @@ You may also be interested in the [grammar]. ## Unicode productions -A few productions in Rust's grammar permit Unicode code points outside the ASCII -range. We define these productions in terms of character properties specified -in the Unicode standard, rather than in terms of ASCII-range code points. The -section [Special Unicode Productions](#special-unicode-productions) lists these -productions. +A few productions in Rust's grammar permit Unicode code points outside the +ASCII range. We define these productions in terms of character properties +specified in the Unicode standard, rather than in terms of ASCII-range code +points. The grammar has a [Special Unicode Productions][unicodeproductions] +section that lists these productions. + +[unicodeproductions]: grammar.html#special-unicode-productions ## String table productions Some rules in the grammar — notably [unary operators](#unary-operator-expressions), [binary -operators](#binary-operator-expressions), and [keywords](#keywords) — are +operators](#binary-operator-expressions), and [keywords][keywords] — are given in a simplified form: as a listing of a table of unquoted, printable whitespace-separated strings. These cases form a subset of the rules regarding the [token](#tokens) rule, and are assumed to be the result of a lexical-analysis phase feeding the parser, driven by a DFA, operating over the disjunction of all such string table entries. +[keywords]: grammar.html#keywords + When such a string enclosed in double-quotes (`"`) occurs inside the grammar, it is an implicit reference to a single member of such a string table production. See [tokens](#tokens) for more information. @@ -75,7 +79,7 @@ An identifier is any nonempty Unicode[^non_ascii_idents] string of the following - The first character has property `XID_start` - The remaining characters have property `XID_continue` -that does _not_ occur in the set of [keywords](#keywords). +that does _not_ occur in the set of [keywords][keywords]. > **Note**: `XID_start` and `XID_continue` as character properties cover the > character ranges used to form the more familiar C and Java language-family @@ -401,7 +405,7 @@ Symbols are a general class of printable [token](#tokens) that play structural roles in a variety of grammar productions. They are catalogued here for completeness as the set of remaining miscellaneous printable tokens that do not otherwise appear as [unary operators](#unary-operator-expressions), [binary -operators](#binary-operator-expressions), or [keywords](#keywords). +operators](#binary-operator-expressions), or [keywords][keywords]. ## Paths @@ -611,7 +615,7 @@ module needs its own source file: [module definitions](#modules) can be nested within one file. Each source file contains a sequence of zero or more `item` definitions, and -may optionally begin with any number of [attributes](#Items and attributes) +may optionally begin with any number of [attributes](#items-and-attributes) that apply to the containing module, most of which influence the behavior of the compiler. The anonymous crate module can have additional attributes that apply to the crate as a whole. @@ -653,7 +657,7 @@ There are several kinds of item: * [`use` declarations](#use-declarations) * [modules](#modules) * [functions](#functions) -* [type aliases](#type-aliases) +* [type definitions](grammar.html#type-definitions) * [structures](#structures) * [enumerations](#enumerations) * [constant items](#constant-items) @@ -773,7 +777,7 @@ extern crate std as ruststd; // linking to 'std' under another name A _use declaration_ creates one or more local name bindings synonymous with some other [path](#paths). Usually a `use` declaration is used to shorten the path required to refer to a module item. These declarations may appear at the -top of [modules](#modules) and [blocks](#blocks). +top of [modules](#modules) and [blocks](grammar.html#block-expressions). > **Note**: Unlike in many languages, > `use` declarations in Rust do *not* declare linkage dependency with external crates. @@ -1144,9 +1148,7 @@ let px: i32 = match p { Point(x, _) => x }; ``` A _unit-like struct_ is a structure without any fields, defined by leaving off -the list of fields entirely. Such types will have a single value, just like -the [unit value `()`](#unit-and-boolean-literals) of the unit type. For -example: +the list of fields entirely. Such types will have a single value. For example: ``` struct Cookie; @@ -2436,11 +2438,6 @@ comma: (0); // zero in parentheses ``` -### Unit expressions - -The expression `()` denotes the _unit value_, the only value of the type with -the same name. - ### Structure expressions There are several forms of structure expressions. A _structure expression_ @@ -3281,7 +3278,7 @@ constructor or `struct` field may refer, directly or indirectly, to the enclosing `enum` or `struct` type itself. Such recursion has restrictions: * Recursive types must include a nominal type in the recursion - (not mere [type definitions](#type-definitions), + (not mere [type definitions](grammar.html#type-definitions), or other structural types such as [arrays](#array,-and-slice-types) or [tuples](#tuple-types)). * A recursive `enum` item must have at least one non-recursive constructor (in order to give the recursion a basis case). From e66653bee52b87ada8d591b13b915e5ed2c86d5a Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Sun, 10 May 2015 20:22:06 -0400 Subject: [PATCH 20/20] TRPL: the stack and the heap --- src/doc/trpl/the-stack-and-the-heap.md | 569 ++++++++++++++++++++++++- 1 file changed, 568 insertions(+), 1 deletion(-) diff --git a/src/doc/trpl/the-stack-and-the-heap.md b/src/doc/trpl/the-stack-and-the-heap.md index cc0941bc025aa..7b1cd7dc8093b 100644 --- a/src/doc/trpl/the-stack-and-the-heap.md +++ b/src/doc/trpl/the-stack-and-the-heap.md @@ -1,3 +1,570 @@ % The Stack and the Heap -Coming Soon +As a systems language, Rust operates at a low level. If you’re coming from a +high-level language, there are some aspects of systems programming that you may +not be familiar with. The most important one is how memory works, with a stack +and a heap. If you’re familiar with how C-like languages use stack allocation, +this chapter will be a refresher. If you’re not, you’ll learn about this more +general concept, but with a Rust-y focus. + +# Memory management + +These two terms are about memory management. The stack and the heap are +abstractions that help you determine when to allocate and deallocate memory. + +Here’s a high-level comparison: + +The stack is very fast, and is where memory is allocated in Rust by default. +But the allocation is local to a function call, and is limited in size. The +heap, on the other hand, is slower, and is explicitly allocated by your +program. But it’s effectively unlimited in size, and is globally accessible. + +# The Stack + +Let’s talk about this Rust program: + +```rust +fn main() { + let x = 42; +} +``` + +This program has one variable binding, `x`. This memory needs to be allocated +from somewhere. Rust ‘stack allocates’ by default, which means that basic +values ‘go on the stack’. What does that mean? + +Well, when a function gets called, some memory gets allocated for all of its +local variables and some other information. This is called a ‘stack frame’, and +for the purpose of this tutorial, we’re going to ignore the extra information +and just consider the local variables we’re allocating. So in this case, when +`main()` is run, we’ll allocate a single 32-bit integer for our stack frame. +This is automatically handled for you, as you can see, we didn’t have to write +any special Rust code or anything. + +When the function is over, its stack frame gets deallocated. This happens +automatically, we didn’t have to do anything special here. + +That’s all there is for this simple program. The key thing to understand here +is that stack allocation is very, very fast. Since we know all the local +variables we have ahead of time, we can grab the memory all at once. And since +we’ll throw them all away at the same time as well, we can get rid of it very +fast too. + +The downside is that we can’t keep values around if we need them for longer +than a single function. We also haven’t talked about what that name, ‘stack’ +means. To do that, we need a slightly more complicated example: + +```rust +fn foo() { + let y = 5; + let z = 100; +} + +fn main() { + let x = 42; + + foo(); +} +``` + +This program has three variables total: two in `foo()`, one in `main()`. Just +as before, when `main()` is called, a single integer is allocated for its stack +frame. But before we can show what happens when `foo()` is called, we need to +visualize what’s going on with memory. Your operating system presents a view of +memory to your program that’s pretty simple: a huge list of addresses, from 0 +to a large number, representing how much RAM your computer has. For example, if +you have a gigabyte of RAM, your addresses go from `0` to `1,073,741,824`. That +number comes from 230, the number of bytes in a gigabyte. + +This memory is kind of like a giant array: addresses start at zero and go +up to the final number. So here’s a diagram of our first stack frame: + +| Address | Name | Value | ++---------+------+-------+ +| 0 | x | 42 | + +We’ve got `x` located at address `0`, with the value `42`. + +When `foo()` is called, a new stack frame is allocated: + +| Address | Name | Value | ++---------+------+-------+ +| 2 | z | 100 | +| 1 | y | 5 | +| 0 | x | 42 | + +Because `0` was taken by the first frame, `1` and `2` are used for `foo()`’s +stack frame. It grows upward, the more functions we call. + + +There’s some important things we have to take note of here. The numbers 0, 1, +and 2 are all solely for illustrative purposes, and bear no relationship to the +actual numbers the computer will actually use. In particular, the series of +addresses are in reality going to be separated by some number of bytes that +separate each address, and that separation may even exceed the size of the +value being stored. + +After `foo()` is over, its frame is deallocated: + +| Address | Name | Value | ++---------+------+-------+ +| 0 | x | 42 | + +And then, after `main()`, even this last value goes away. Easy! + +It’s called a ‘stack’ because it works like a stack of dinner plates: the first +plate you put down is the last plate to pick back up. Stacks are sometimes +called ‘last in, first out queues’ for this reason, as the last value you put +on the stack is the first one you retrieve from it. + +Let’s try a three-deep example: + +```rust +fn bar() { + let i = 6; +} + +fn foo() { + let a = 5; + let b = 100; + let c = 1; + + bar(); +} + +fn main() { + let x = 42; + + foo(); +} +``` + +Okay, first, we call `main()`: + +| Address | Name | Value | ++---------+------+-------+ +| 0 | x | 42 | + +Next up, `main()` calls `foo()`: + +| Address | Name | Value | ++---------+------+-------+ +| 3 | c | 1 | +| 2 | b | 100 | +| 1 | a | 5 | +| 0 | x | 42 | + +And then `foo()` calls `bar()`: + +| Address | Name | Value | ++---------+------+-------+ +| 4 | i | 6 | +| 3 | c | 1 | +| 2 | b | 100 | +| 1 | a | 5 | +| 0 | x | 42 | + +Whew! Our stack is growing tall. + +After `bar()` is over, its frame is deallocated, leaving just `foo()` and +`main()`: + +| Address | Name | Value | ++---------+------+-------+ +| 3 | c | 1 | +| 2 | b | 100 | +| 1 | a | 5 | +| 0 | x | 42 | + +And then `foo()` ends, leaving just `main()` + +| Address | Name | Value | ++---------+------+-------+ +| 0 | x | 42 | + +And then we’re done. Getting the hang of it? It’s like piling up dishes: you +add to the top, you take away from the top. + +# The Heap + +Now, this works pretty well, but not everything can work like this. Sometimes, +you need to pass some memory between different functions, or keep it alive for +longer than a single function’s execution. For this, we can use the heap. + +In Rust, you can allocate memory on the heap with the [`Box` type][box]. +Here’s an example: + +```rust +fn main() { + let x = Box::new(5); + let y = 42; +} +``` + +[box]: ../std/boxed/index.html + +Here’s what happens in memory when `main()` is called: + +| Address | Name | Value | ++---------+------+--------+ +| 1 | y | 42 | +| 0 | x | ?????? | + +We allocate space for two variables on the stack. `y` is `42`, as it always has +been, but what about `x`? Well, `x` is a `Box`, and boxes allocate memory +on the heap. The actual value of the box is a structure which has a pointer to +‘the heap’. When we start executing the function, and `Box::new()` is called, +it allocates some memory for the heap, and puts `5` there. The memory now looks +like this: + +| Address | Name | Value | ++-----------------+------+----------------+ +| 230 | | 5 | +| ... | ... | ... | +| 1 | y | 42 | +| 0 | x | 230 | + +We have 230 in our hypothetical computer with 1GB of RAM. And since +our stack grows from zero, the easiest place to allocate memory is from the +other end. So our first value is at the highest place in memory. And the value +of the struct at `x` has a [raw pointer][rawpointer] to the place we’ve +allocated on the heap, so the value of `x` is 230, the memory +location we’ve asked for. + +[rawpointer]: raw-pointers.html + +We haven’t really talked too much about what it actually means to allocate and +deallocate memory in these contexts. Getting into very deep detail is out of +the scope of this tutorial, but what’s important to point out here is that +the heap isn’t just a stack that grows from the opposite end. We’ll have an +example of this later in the book, but because the heap can be allocated and +freed in any order, it can end up with ‘holes’. Here’s a diagram of the memory +layout of a program which has been running for a while now: + + +| Address | Name | Value | ++----------------------+------+----------------------+ +| 230 | | 5 | +| (230) - 1 | | | +| (230) - 2 | | | +| (230) - 3 | | 42 | +| ... | ... | ... | +| 3 | y | (230) - 3 | +| 2 | y | 42 | +| 1 | y | 42 | +| 0 | x | 230 | + +In this case, we’ve allocated four things on the heap, but deallocated two of +them. There’s a gap between 230 and (230) - 3 which isn’t +currently being used. The specific details of how and why this happens depends +on what kind of strategy you use to manage the heap. Different programs can use +different ‘memory allocators’, which are libraries that manage this for you. +Rust programs use [jemalloc][jemalloc] for this purpose. + +[jemalloc]: http://www.canonware.com/jemalloc/ + +Anyway, back to our example. Since this memory is on the heap, it can stay +alive longer than the function which allocates the box. In this case, however, +it doesn’t.[^moving] When the function is over, we need to free the stack frame +for `main()`. `Box`, though, has a trick up its sleve: [Drop][drop]. The +implementation of `Drop` for `Box` deallocates the memory that was allocated +when it was created. Great! So when `x` goes away, it first frees the memory +allocated on the heap: + +| Address | Name | Value | ++---------+------+--------+ +| 1 | y | 42 | +| 0 | x | ?????? | + +[drop]: drop.html +[moving]: We can make the memory live longer by transferring ownership, + sometimes called ‘moving out of the box’. More complex examples will + be covered later. + + +And then the stack frame goes away, freeing all of our memory. + +# Arguments and borrowing + +We’ve got some basic examples with the stack and the heap going, but what about +function arguments and borrowing? Here’s a small Rust program: + +```rust +fn foo(i: &i32) { + let z = 42; +} + +fn main() { + let x = 5; + let y = &x; + + foo(y); +} +``` + +When we enter `main()`, memory looks like this: + +| Address | Name | Value | ++---------+------+-------+ +| 1 | y | 0 | +| 0 | x | 5 | + +`x` is a plain old `5`, and `y` is a reference to `x`. So its value is the +memory location that `x` lives at, which in this case is `0`. + +What about when we call `foo()`, passing `y` as an argument? + +| Address | Name | Value | ++---------+------+-------+ +| 3 | z | 42 | +| 2 | i | 0 | +| 1 | y | 0 | +| 0 | x | 5 | + +Stack frames aren’t just for local bindings, they’re for arguments too. So in +this case, we need to have both `i`, our argument, and `z`, our local variable +binding. `i` is a copy of the argument, `y`. Since `y`’s value is `0`, so is +`i`’s. + +This is one reason why borrowing a variable doesn’t deallocate any memory: the +value of a reference is just a pointer to a memory location. If we got rid of +the underlying memory, things wouldn’t work very well. + +# A complex example + +Okay, let’s go through this complex program step-by-step: + +```rust +fn foo(x: &i32) { + let y = 10; + let z = &y; + + baz(z); + bar(x, z); +} + +fn bar(a: &i32, b: &i32) { + let c = 5; + let d = Box::new(5); + let e = &d; + + baz(e); +} + +fn baz(f: &i32) { + let g = 100; +} + +fn main() { + let h = 3; + let i = Box::new(20); + let j = &h; + + foo(j); +} +``` + +First, we call `main()`: + +| Address | Name | Value | ++-----------------+------+----------------+ +| 230 | | 20 | +| ... | ... | ... | +| 2 | j | 0 | +| 1 | i | 230 | +| 0 | h | 3 | + +We allocate memory for `j`, `i`, and `h`. `i` is on the heap, and so has a +value pointing there. + +Next, at the end of `main()`, `foo()` gets called: + +| Address | Name | Value | ++-----------------+------+----------------+ +| 230 | | 20 | +| ... | ... | ... | +| 5 | z | 4 | +| 4 | y | 10 | +| 3 | x | 0 | +| 2 | j | 0 | +| 1 | i | 230 | +| 0 | h | 3 | + +Space gets allocated for `x`, `y`, and `z`. The argument `x` has the same value +as `j`, since that’s what we passed it in. It’s a pointer to the `0` address, +since `j` points at `h`. + +Next, `foo()` calls `baz()`, passing `z`: + +| Address | Name | Value | ++-----------------+------+----------------+ +| 230 | | 20 | +| ... | ... | ... | +| 7 | g | 100 | +| 6 | f | 4 | +| 5 | z | 4 | +| 4 | y | 10 | +| 3 | x | 0 | +| 2 | j | 0 | +| 1 | i | 230 | +| 0 | h | 3 | + +We’ve allocated memory for `f` and `g`. `baz()` is very short, so when it’s +over, we get rid of its stack frame: + +| Address | Name | Value | ++-----------------+------+----------------+ +| 230 | | 20 | +| ... | ... | ... | +| 5 | z | 4 | +| 4 | y | 10 | +| 3 | x | 0 | +| 2 | j | 0 | +| 1 | i | 230 | +| 0 | h | 3 | + +Next, `foo()` calls `bar()` with `x` and `z`: + +| Address | Name | Value | ++----------------------+------+----------------------+ +| 230 | | 20 | +| (230) - 1 | | 5 | +| ... | ... | ... | +| 10 | e | 4 | +| 9 | d | (230) - 1 | +| 8 | c | 5 | +| 7 | b | 4 | +| 6 | a | 0 | +| 5 | z | 4 | +| 4 | y | 10 | +| 3 | x | 0 | +| 2 | j | 0 | +| 1 | i | 230 | +| 0 | h | 3 | + +We end up allocating another value on the heap, and so we have to subtract one +from 230. It’s easier to just write that than `1,073,741,823`. In any +case, we set up the variables as usual. + +At the end of `bar()`, it calls `baz()`: + +| Address | Name | Value | ++----------------------+------+----------------------+ +| 230 | | 20 | +| (230) - 1 | | 5 | +| ... | ... | ... | +| 12 | g | 100 | +| 11 | f | 4 | +| 10 | e | 4 | +| 9 | d | (230) - 1 | +| 8 | c | 5 | +| 7 | b | 4 | +| 6 | a | 0 | +| 5 | z | 4 | +| 4 | y | 10 | +| 3 | x | 0 | +| 2 | j | 0 | +| 1 | i | 230 | +| 0 | h | 3 | + +With this, we’re at our deepest point! Whew! Congrats for following along this +far. + +After `baz()` is over, we get rid of `f` and `g`: + +| Address | Name | Value | ++----------------------+------+----------------------+ +| 230 | | 20 | +| (230) - 1 | | 5 | +| ... | ... | ... | +| 10 | e | 4 | +| 9 | d | (230) - 1 | +| 8 | c | 5 | +| 7 | b | 4 | +| 6 | a | 0 | +| 5 | z | 4 | +| 4 | y | 10 | +| 3 | x | 0 | +| 2 | j | 0 | +| 1 | i | 230 | +| 0 | h | 3 | + +Next, we return from `bar()`. `d` in this case is a `Box`, so it also frees +what it points to: (230) - 1. + +| Address | Name | Value | ++-----------------+------+----------------+ +| 230 | | 20 | +| ... | ... | ... | +| 5 | z | 4 | +| 4 | y | 10 | +| 3 | x | 0 | +| 2 | j | 0 | +| 1 | i | 230 | +| 0 | h | 3 | + +And after that, `foo()` returns: + +| Address | Name | Value | ++-----------------+------+----------------+ +| 230 | | 20 | +| ... | ... | ... | +| 2 | j | 0 | +| 1 | i | 230 | +| 0 | h | 3 | + +And then, finally, `main()`, which cleans the rest up. When `i` is `Drop`ped, +it will clean up the last of the heap too. + +# What do other languages do? + +Most languages with a garbage collector heap-allocate by default. This means +that every value is boxed. There are a number of reasons why this is done, but +they’re out of scope for this tutorial. There are some possible optimizations +that don’t make it true 100% of the time, too. Rather than relying on the stack +and `Drop` to clean up memory, the garbage collector deals with the heap +instead. + +# Which to use? + +So if the stack is faster and easier to manage, why do we need the heap? A big +reason is that Stack-allocation alone means you only have LIFO semantics for +reclaiming storage. Heap-allocation is strictly more general, allowing storage +to be taken from and returned to the pool in arbitrary order, but at a +complexity cost. + +Generally, you should prefer stack allocation, and so, Rust stack-allocates by +default. The LIFO model of the stack is simpler, at a fundamental level. This +has two big impacts: runtime efficiency and semantic impact. + +## Runtime Efficiency. + +Managing the memory for the stack is trivial: The machine just +increments or decrements a single value, the so-called “stack pointer”. +Managing memory for the heap is non-trivial: heap-allocated memory is freed at +arbitrary points, and each block of heap-allocated memory can be of arbitrary +size, the memory manager must generally work much harder to identify memory for +reuse. + +If you’d like to dive into this topic in greater detail, [this paper][wilson] +is a great introduction. + +[wilson]: http://www.cs.northwestern.edu/~pdinda/icsclass/doc/dsa.pdf + +## Semantic impact + +Stack-allocation impacts the Rust language itself, and thus the developer’s +mental model. The LIFO semantics is what drives how the Rust language handles +automatic memory management. Even the deallocation of a uniquely-owned +heap-allocated box can be driven by the stack-based LIFO semantics, as +discussed throughout this chapter. The flexibility (i.e. expressiveness) of non +LIFO-semantics means that in general the compiler cannot automatically infer at +compile-time where memory should be freed; it has to rely on dynamic protocols, +potentially from outside the language itself, to drive deallocation (reference +counting, as used by `Rc` and `Arc`, is one example of this). + +When taken to the extreme, the increased expressive power of heap allocation +comes at the cost of either significant runtime support (e.g. in the form of a +garbage collector) or significant programmer effort (in the form of explicit +memory management calls that require verification not provided by the Rust +compiler).