Go does not prevent you from sharing memory between goroutines/threads. What they mean by communicating, is that you send a chunk of data, or a pointer to said chunk, across a channel. This effectively transfers 'ownership' of the data to the target reader of the channel. Mind you, this transfer of ownership is not enforced by the language or the runtime, it is just by convention.
You are still perfectly capable of writing to the same memory from two goroutines, if you so choose. In other words: Go does not prevent you from shooting yourself in the foot, it just provides language semantics which make these mistakes easier to detect.
If a value is passed into a channel, the programmer must then assume that value is no longer his to write to in the same goroutine.
func F(c chan *T) {
// Create/load some data.
data := getSomeData()
// Send data into the channel.
c <- data
// 'data' should now be considered out-of-bounds for the remainder of
// this function. This is purely by convention, and is not enforced
// anywhere. For example, the following is still valid Go code, but will
// lead to problems.
data.Field = 123
}