• Stars
    star
    138
  • Rank 264,508 (Top 6 %)
  • Language
    Go
  • License
    MIT License
  • Created over 8 years ago
  • Updated over 8 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

An immutability helper for Go

Freeze

GoDoc Go Report Card

go get github.com/lukechampine/freeze

Package freeze enables the "freezing" of data, similar to JavaScript's Object.freeze(). A frozen object cannot be modified; attempting to do so will result in an unrecoverable panic.

Freezing is useful for providing soft guarantees of immutability. That is: the compiler can't prevent you from mutating an frozen object, but the runtime can. One of the unfortunate aspects of Go is its limited support for constants: structs, slices, and even arrays cannot be declared as consts. This becomes a problem when you want to pass a slice around to many consumers without worrying about them modifying it. With freeze, you can guard against these unwanted or intended behaviors.

To accomplish this, the mprotect syscall is used. Sadly, this necessitates allocating new memory via mmap and copying the data into it. This performance penalty should not be prohibitive, but it's something to be aware of.

In case it wasn't clear from the previous paragraph, this package is not intended to be used in production. A well-designed API is a much saner solution than freezing your data structures. I would even caution against using freeze in your automated testing, due to its platform-specific nature. freeze is best used for "one-off" debugging. Something like this:

  1. Observe bug
  2. Suspect that shared mutable data is the culprit
  3. Call freeze.Object on the data after it is created
  4. Run program again; it crashes
  5. Inspect stack trace to identify where the data was modified
  6. Fix bug
  7. Remove call to freeze.Object

Again: do not use freeze in production. It's a cool proof-of-concept, and it can be useful for debugging, but that's about it. Let me put it another way: freeze imports four packages: reflect, runtime, unsafe, and syscall (actually golang.org/x/sys/unix). Does that sound like a package you want to depend on?

Okay, back to the real documention:

Functions are provided for freezing the three "pointer types:" Pointer, Slice, and Map. Each function returns a copy of their input that is backed by protected memory. In addition, Object is provided for freezing recursively. Given a slice of pointers, Object will prevent modifications to both the pointer data and the slice data, while Slice merely does the latter.

To freeze an object:

type foo struct {
	X int
	y bool // yes, freeze works on unexported fields!
}
f := &foo{3, true}
f = freeze.Object(f).(*foo)
println(f.X) // ok; prints 3
f.X++        // not ok; panics

Note that since foo does not contain any pointers, calling Pointer(f) would have the same effect here.

It is recommended that, where convenient, you reassign the return value to its original variable, as with append. Otherwise, you will retain both the mutable original and the frozen copy.

Likewise, to freeze a slice:

xs := []int{1, 2, 3}
xs = freeze.Slice(xs).([]int)
println(xs[0]) // ok; prints 1
xs[0]++        // not ok; panics

Interfaces can also be frozen, since internally they are just pointers to objects. The effect of this is that the interface's pure methods can still be called, but impure methods cannot. Unfortunately, the impurity of a given method is defined by the implementation, not the interface. Even a String() method could conceivably modify some internal state. Furthermore, the caveat about unexported struct fields (see below) applies here, so many exported objects cannot be completely frozen.

Caveats

This package depends heavily on the internal representations of the slice and map types. These objects are not likely to change, but if they do, this package will break.

In general, you can't call Object on the same object twice. This is because Object will attempt to rewrite the object's internal pointers -- which is a memory modification. Calling Pointer or Slice twice should be fine.

Object cannot descend into unexported struct fields. It can still freeze the field itself, but if the field contains a pointer, the data it points to will not be frozen.

Appending to a frozen slice will trigger a panic iff len(slice) < cap(slice). This is because appending to a full slice will allocate new memory.

Unix is the only supported platform. Windows support is not planned, because it doesn't support a syscall analogous to mprotect.

More Repositories

1

jsteg

JPEG steganography
Go
555
star
2

blake3

A pure-Go implementation of the BLAKE3 cryptographic hash function
Assembly
314
star
3

geiger

A Geiger counter for allocations
Go
269
star
4

uint128

uint128 for Go
Go
212
star
5

stm

Software Transactional Memory in Go
Go
190
star
6

ply

Painless polymorphism
Go
119
star
7

frand

A fast userspace CSPRNG
Go
81
star
8

lthash

A homomorphic hash function
Go
70
star
9

fastxor

The fastest way to xor bytes in Go
Go
69
star
10

few

Fastest Encoder in the West
Go
64
star
11

randmap

Truly random map access and iteration for Go (OUTDATED -- FOR EDUCATIONAL PURPOSES ONLY)
Go
61
star
12

flagg

Barebones subcommand handling
Go
56
star
13

intersort

Sort anything.
Go
55
star
14

us

An alternative interface to Sia
Go
54
star
15

noescape

Promise to the Go compiler that your Reads and Writes are well-behaved
Go
51
star
16

advent

Advent of Code solutions
Go
38
star
17

rote

A flashcard app for Landscape
JavaScript
31
star
18

goldilocks

Find your habitable zone
JavaScript
28
star
19

hey

Hey!
Go
22
star
20

slouch

A competitive programming language (WIP -- MAY CONTAIN HIDEOUS CODE)
Go
20
star
21

jr

A tiny Command Line Interface JavaScript Object Notation Remote Procedure Call client
Go
16
star
22

user

A CLI renter for Sia
Go
12
star
23

walrus

A wallet server for Sia
Go
12
star
24

go-urbit

Go + Urbit
Go
11
star
25

muse

A contract server for Sia
Go
10
star
26

ascon

Go
6
star
27

httprpc

An HTTP wrapper for net/rpc
Go
6
star
28

ADHN

Inline summaries on the HN front page
JavaScript
5
star
29

nock

A simple Nock interpreter in Go
Go
5
star
30

caller

Nicely-formatted callstack strings
Go
5
star
31

walrus-cli

A client for the walrus wallet server
Go
3
star
32

mkvsynth

Linux Video Processing
C
3
star
33

Streaming-Merkle-Trees-Paper

TeX
2
star
34

tenten

A 1010! AI
Go
2
star
35

pool

Go
2
star
36

threadgroup

A shutdown facilitator for Go
Go
2
star
37

shard

A host announcement relay daemon for Sia
Go
2
star
38

dispel

A searchable online image database
Go
2
star
39

jj

The JSON Journal
Go
1
star
40

silicon

modal fork of sandy
C
1
star
41

0hh1

Automated solver for http://0hh1.com
Go
1
star
42

algo

Fooling around with animation in Go
Go
1
star
43

sialedger

Go interface to the Sia Ledger Nano S app
Go
1
star
44

upnp

Go
1
star
45

us-bindings

Various bindings for https://github.com/lukechampine/us
Go
1
star
46

mjson

Modify JSON super fast
Go
1
star