Randomize the Elements of a Byte Slice in One Line

I've never had to generate a random byte slice before 1.6; I'd imagine that there'd be a loop involved where each iteration involves assigning a random value to every element:

for index, _ := range randomByteSlice {  
             randomByteSlice[index] = byte(rand.Int())
}

The release notes for Go 1.6 includes the addition of a function to the math/rand package which populates a slice of bytes with random values. This function is noticeably faster than iteration. For cryptographic settings, the crypto/rand package is recommended.

Observe the function's signature and description:

func Read(p []byte) (n int, err error)

Read generates len(p) random bytes from the default Source and writes them into p. It always returns len(p) and a nil error.  

Now, we could populate a byte slice of arbitrary length with random values like so:

randomContent := make([]byte, 100)

rand.Read(randomContent)  

I did leave out error checking on the return values for simplicity. Interestingly enough, they aren't calculated. In the source of the function, the return values, number of bytes written + error, are hardcoded to be the length of the slice and nil, respectively.

Knowing this, it seems that the value of bytes written is informational at best. Moreover, checking for a non nil error seems pointless. Why check for errors at all? See the optional, next section for my two cents on that.

There is a Read() method that was also added which applies the same functionality on an a *Rand, so you're not just limited to the package wide Rand() function.

Lastly, you can view the complete, executable examples of the code snippets on the blog repo here. Happy Friday!


More on the Return Values of Read()


The Rabbit hole

The description of Read mentioned that it, "will always return the len(p) and a nil error." The term always bugged me, so I decided to look at at the source, and it was just as the docs said:

func (r *Rand) Read(p []byte) (n int, err error) {  
       for i := 0; i < len(p); i += 7 {
               val := r.src.Int63()
               for j := 0; i+j < len(p) && j < 7; j++ {
                       p[i+j] = byte(val)
                       val >>= 8
               }
       }
       return len(p), nil
}


My Two Cents

While disregarding the return values might be valid, there should probably still be code that handles them.

Recall that the number one rule is to never break backwards compatibility. Also, the style of similarly functioning methods and functions in other packages (Read/Write) return the exact same two bits of information.

With this in mind, it makes sense that there should still be code to check the return values in the event that future changes to the algorithm may produce a non nil error, or write a number of bytes that may differ from the length of the argument slice.

Have another opinion? Have you seen something like this before in other packages? Let me know! Thanks.