This proposal is a combination of two proposals to evolve Golang towards a 
capabilties-based language.

   1. Modify the const keyword to allow holding mutable (and new immutable) 
   types.
   2. Implement faster immutable struct assignment.

In Rust, immutability is deep, so you can't modify the internals of any 
object if your reference is not Mut. From 
https://doc.rust-lang.org/book/first-edition/mutability.html:

Mutability is a property of either a borrow (&mut) or a binding (let mut). 
This means that, for example, you cannot have a struct with some fields 
mutable and some immutable: The mutability of a struct is in its binding.

In Go, the design philosophy includes embeddable encapsulation. We can 
leverage the language rules to create a capabilities-based language. The 
addressable (mutable) vs non-addressable (immutable) struct semantics takes 
us 90% of the way there. What's missing I think are 2 things:

   1. Secure modules w/ the const-mutable type
   2. Compiler optimizations for immutable struct copying.

<https://gist.github.com/jaekwon/fc0e700f0cb09ea95fe9d6655f98b57e#1-secure-modules-w-the-const-mutable-type>1.
 
Secure modules w/ the const-mutable type

The prososal is to allow what would otherwise be assignable to a var, to 
const (but disallow function calls).

// These are all OK
const MyArray = make([]byte, 10)          // const mutable (slice) type (all 
slices are mutable)
const MyStruct = MyStruct{}               // const immutable (struct) type
const MyStructP = &MyStruct{}             // const mutable (struct) type
const MyStruct MyInterface = MyStruct{}   // const immutable (interface) type 
w/ struct value
const MyStructP MyInterface = &MyStruct{} // const mutable (interface) type w/ 
pointer value
const MyFunc = func(){...}                // const immutable func type (all 
funcs are immutable)

Here, const doesn't mean immutable. It just means you can't change the 
shallow value.

There is already a distinction between typed and untyped consts. This only 
works for typed consts, and further introduces a distinction between 
const-mutable const-immutable types. While the procedural behavior of const 
func types are always "immutable", they may have side effects.

The right hand side expression of a const declaration may not include a 
function *call*. This limits the scope of these new const-mutable and 
const-immutable to those that won't cause a const initialization fiasco 
(see https://groups.google.com/forum/m/#!topic/golang-nuts/BnjG3N77Ico).

The type of values you can assign to a const are more expressive, so we now 
have no excuse to declare module-level var variables which can be 
(maliciously or not) modified by anyone who imports the module.

(Of course we can declare a global function func GetMyStructP() *MyStruct { 
... }) that more or less does the same thing, but nobody does that because 
it's easier to use var, and what's the point of doing the right thing 
unless there's a commitment to evolve the language toward a 
capabilities-based system?).

Go linters can start tagging exported global vars that aren't annotated to 
be safe to mutate by anyone.
<https://gist.github.com/jaekwon/fc0e700f0cb09ea95fe9d6655f98b57e#compiler-optimizations-for-immutable-struct-copying>Compiler
 
optimizations for immutable struct copying.

You might have seen the rule of thumb for values-vs-pointers: if your 
struct has few fields, and you don’t need to mutate it, then you don’t need 
to use pointers. On the other hand, if the struct has too many fields, then 
you might want to use pointers.

I find it difficult to always tell ahead of time whether structs ought to 
be pointers or not, but recently I discovered a nice way to work around the 
performance problem:

type BigStructWrapper interface {}
type BigStruct struct { ... }

// cpy is immutable but this is slow.
// I've tested this w/ benchmarks and very large
// structs that are nested many levels.
{
  var str BigStruct = BigStruct{...}
  var cpy BigStruct = str
}

// cpy is still immutable and this is fast.
{
  var str BigStructWrapper = BigStruct{...}
  var cpy BigStructWrapper = str
}

Interface values are like pointers, but the value of an interface is not 
addressable so you can't mutate an interface value's shallow fields unless 
it's a pointer.

But a non-pointer struct is immutable, so I suspect it's possible for 
Golang to optimize the copying/assignment of these structs by allocating 
them somewhere outside the stack with ref counting. Then we have a complete 
capabilities-based security model for Golang.

(1) Forget the old rule about using pointers when the struct has too many 
fields. Always use non-pointer values if you don't want others to mutate 
your copy.

(2) Do not expose anything that can be mutated by a malicious party, even 
the users of your module.

My hope is that Golang can evolve in this way, and even evolve the plugin 
system, such that we can allow the plugging-in of relatively untrusted 
(e.g. after filtering the AST for bad imports etc) code (see 
https://golang.org/pkg/plugin/). This is already useful but if we also 
allow for plugins to become released, then I think that's everything we 
need to create safe runtime extensibility of Golang services.  We'd 
certainly use it for [Tendermint](https://github.com/tendermint/tendermint).

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to