diff --git a/doc/langref.html.in b/doc/langref.html.in index b8ee292462..8e6f22a049 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -7928,13 +7928,261 @@ pub fn main() void { {#header_close#} {#header_open|Memory#} -
TODO: explain no default allocator in zig
-TODO: show how to use the allocator interface
-TODO: mention debug allocator
-TODO: importance of checking for allocation failure
-TODO: mention overcommit and the OOM Killer
-TODO: mention recursion
- {#see_also|Pointers#} ++ The Zig language performs no memory management on behalf of the programmer. This is + why Zig has no runtime, and why Zig code works seamlessly in so many environments, + including real-time software, operating system kernels, embedded devices, and + low latency servers. As a consequence, Zig programmers must always be able to answer + the question: +
+{#link|Where are the bytes?#}
+
+ Like Zig, the C programming language has manual memory management. However, unlike Zig,
+ C has a default allocator - malloc, realloc, and free.
+ When linking against libc, Zig exposes this allocator with {#syntax#}std.heap.c_allocator{#endsyntax#}.
+ However, by convention, there is no default allocator in Zig. Instead, functions which need to
+ allocate accept an {#syntax#}*Allocator{#endsyntax#} parameter. Likewise, data structures such as
+ {#syntax#}std.ArrayList{#endsyntax#} accept an {#syntax#}*Allocator{#endsyntax#} parameter in
+ their initialization functions:
+
+ In the above example, 100 bytes of stack memory are used to initialize a + {#syntax#}FixedBufferAllocator{#endsyntax#}, which is then passed to a function. + As a convenience there is a global {#syntax#}FixedBufferAllocator{#endsyntax#} + available for quick tests at {#syntax#}std.debug.global_allocator{#endsyntax#}, + however it is deprecated and should be avoided in favor of directly using a + {#syntax#}FixedBufferAllocator{#endsyntax#} as in the example above. +
++ Currently Zig has no general purpose allocator, but there is + one under active development. + Once it is merged into the Zig standard library it will become available to import + with {#syntax#}std.heap.default_allocator{#endsyntax#}. However, it will still be recommended to + follow the {#link|Choosing an Allocator#} guide. +
+ + {#header_open|Choosing an Allocator#} +What allocator to use depends on a number of factors. Here is a flow chart to help you decide: +
+String literals such as {#syntax#}"foo"{#endsyntax#} are in the global constant data section. + This is why it is an error to pass a string literal to a mutable slice, like this: +
+ {#code_begin|test_err|expected type '[]u8'#} +fn foo(s: []u8) void {} + +test "string literal to mutable slice" { + foo("hello"); +} + {#code_end#} +However if you make the slice constant, then it works:
+ {#code_begin|test|strlit#} +fn foo(s: []const u8) void {} + +test "string literal to constant slice" { + foo("hello"); +} + {#code_end#} ++ Just like string literals, `const` declarations, when the value is known at {#link|comptime#}, + are stored in the global constant data section. Also {#link|Compile Time Variables#} are stored + in the global constant data section. +
++ `var` declarations inside functions are stored in the function's stack frame. Once a function returns, + any {#link|Pointers#} to variables in the function's stack frame become invalid references, and + dereferencing them becomes unchecked {#link|Undefined Behavior#}. +
++ `var` declarations at the top level or in {#link|struct#} declarations are stored in the global + data section. +
++ The location of memory allocated with {#syntax#}allocator.alloc{#endsyntax#} or + {#syntax#}allocator.create{#endsyntax#} is determined by the allocator's implementation. +
+ TODO: thread local variables + {#header_close#} + + {#header_open|Implementing an Allocator#} +Zig programmers can implement their own allocators by fulfilling the Allocator interface. + In order to do this one must read carefully the documentation comments in std/mem.zig and + then supply a {#syntax#}reallocFn{#endsyntax#} and a {#syntax#}shrinkFn{#endsyntax#}. +
++ There are many example allocators to look at for inspiration. Look at std/heap.zig and + at this + work-in-progress general purpose allocator. + TODO: once #21 is done, link to the docs + here. +
+ {#header_close#} + + {#header_open|Heap Allocation Failure#} ++ Many programming languages choose to handle the possibility of heap allocation failure by + unconditionally crashing. By convention, Zig programmers do not consider this to be a + satisfactory solution. Instead, {#syntax#}error.OutOfMemory{#endsyntax#} represents + heap allocation failure, and Zig libraries return this error code whenever heap allocation + failure prevented an operation from completing successfully. +
++ Some have argued that because some operating systems such as Linux have memory overcommit enabled by + default, it is pointless to handle heap allocation failure. There are many problems with this reasoning: +
++ Recursion is a fundamental tool in modeling software. However it has an often-overlooked problem: + unbounded memory allocation. +
++ Recursion is an area of active experimentation in Zig and so the documentation here is not final. + You can read a + summary of recursion status in the 0.3.0 release notes. +
++ The short summary is that currently recursion works normally as you would expect. Although Zig code + is not yet protected from stack overflow, it is planned that a future version of Zig will provide + such protection, with some degree of cooperation from Zig code required. +
+ {#header_close#} + + {#header_open|Lifetime and Ownership#} ++ It is the Zig programmer's responsibility to ensure that a {#link|pointer|Pointers#} is not + accessed when the memory pointed to is no longer available. Note that a {#link|slice|Slices#} + is a form of pointer, in that it references other memory. +
++ In order to prevent bugs, there are some helpful conventions to follow when dealing with pointers. + In general, when a function returns a pointer, the documentation for the function should explain + who "owns" the pointer. This concept helps the programmer decide when it is appropriate, if ever, + to free the pointer. +
++ For example, the function's documentation may say "caller owns the returned memory", in which case + the code that calls the function must have a plan for when to free that memory. Probably in this situation, + the function will accept an {#syntax#}*Allocator{#endsyntax#} parameter. +
++ Sometimes the lifetime of a pointer may be more complicated. For example, when using + {#syntax#}std.ArrayList(T).toSlice(){#endsyntax#}, the returned slice has a lifetime that remains + valid until the next time the list is resized, such as by appending new elements. +
++ The API documentation for functions and data structures should take great care to explain + the ownership and lifetime semantics of pointers. Ownership determines whose responsibility it + is to free the memory referenced by the pointer, and lifetime determines the point at which + the memory becomes inaccessible (lest {#link|Undefined Behavior#} occur). +
+ {#header_close#} {#header_close#} {#header_open|Compile Variables#}