# How to Use the CloneVT Method for Deep Copying Protobuf Messages in Go

> Learn how to use the generated CloneVT method for deep copying protobuf messages in Go with protobuf-go-lite. Create complete duplicates of your messages effortlessly.

- Repository: [Aperture Robotics/protobuf-go-lite](https://github.com/aperturerobotics/protobuf-go-lite)
- Tags: how-to-guide
- Published: 2026-02-25

---

**The `protobuf-go-lite` library automatically generates a `CloneVT()` method for every message type that creates a complete deep copy, recursively cloning all nested messages, slices, maps, and byte arrays while leaving the original untouched.**

The `aperturerobotics/protobuf-go-lite` repository provides a lightweight Protocol Buffers implementation for Go that generates efficient `CloneVT` methods for deep copying. Unlike standard library approaches that might share underlying arrays or maps, the generated `CloneVT` method ensures complete independence between the original and copied messages by recursively cloning all reference types.

## What Is the CloneVT Method?

`CloneVT` is a code-generated method created by the `"clone"` feature in `protobuf-go-lite`. When you compile your `.proto` files using this library, every message struct receives two related methods:

- **`CloneVT() *Message`** – Returns a concrete pointer type with a complete deep copy of all fields.
- **`CloneMessageVT() protobuf_go_lite.CloneMessage`** – Returns the generic `CloneMessage` interface, useful for polymorphic code that handles multiple message types.

The method handles **scalar fields** (integers, floats, strings, bools) with direct assignment, while **reference fields** (nested messages, slices, maps, `bytes`, and `oneof` fields) are recursively cloned to prevent shared mutable state.

## How CloneVT Works Under the Hood

The generator logic resides in **[`features/clone/clone.go`](https://github.com/aperturerobotics/protobuf-go-lite/blob/main/features/clone/clone.go)**, specifically in the `generateCloneMethodsForMessage` function (lines 34-40) and the core cloning routine in the `body` function (lines 45-103).

The generated code follows a consistent pattern for every message type:

1. **Nil safety check** – `if m == nil { return (*Message)(nil) }` ensures safe handling of nil receivers.
2. **Allocation** – `r := new(Message)` creates a fresh struct instance.
3. **Scalar copying** – Direct assignment (`r.Field = m.Field`) for primitive types.
4. **Reference cloning** – Recursive calls to `CloneVT()` for nested messages, `slices.Clone()` for repeated scalar fields, and manual loops with element cloning for repeated messages or maps.
5. **Unknown fields preservation** – `slices.Clone(m.unknownFields)` ensures wire-format data is retained.
6. **Return** – `return r` provides the independent copy.

### Field Handling Categories

The generator categorizes fields into three types for optimal cloning performance:

- **Singular message fields** – Calls `rhs.CloneVT()` recursively.
- **Repeated scalar fields** – Uses `slices.Clone(rhs)` since scalar elements are immutable values.
- **Repeated message or map fields** – Iterates with a loop, cloning each element individually (lines 101-108 of [`clone.go`](https://github.com/aperturerobotics/protobuf-go-lite/blob/main/clone.go)).

## Practical Examples

### Basic Deep Copy with Timestamp

The `Timestamp` message in [`types/known/timestamppb/timestamp.pb.go`](https://github.com/aperturerobotics/protobuf-go-lite/blob/main/types/known/timestamppb/timestamp.pb.go) (lines 43-53) demonstrates a simple implementation:

```go
import (
    "fmt"
    "github.com/aperturerobotics/protobuf-go-lite/types/known/timestamppb"
)

func exampleDeepCopy() {
    // Original message
    orig := &timestamppb.Timestamp{
        Seconds: 1620000000,
        Nanos:   123456789,
    }

    // Deep copy – a completely independent instance
    copy := orig.CloneVT()

    // Modifying the copy does not affect the original
    copy.Nanos = 0
    fmt.Println(orig.Nanos) // 123456789
    fmt.Println(copy.Nanos) // 0
}

```

### Cloning Nested Messages and Maps

For complex messages like `Struct` in [`types/known/structpb/struct.pb.go`](https://github.com/aperturerobotics/protobuf-go-lite/blob/main/types/known/structpb/struct.pb.go) (lines 477-495 for `Struct`, lines 498-514 for `Value`), the method handles recursive cloning:

```go
import (
    "fmt"
    pb "github.com/aperturerobotics/protobuf-go-lite/types/known/structpb"
)

func cloneComplex() {
    // Build a nested structure
    orig := &pb.Struct{
        Fields: map[string]*pb.Value{
            "name": {Kind: &pb.Value_StringValue{StringValue: "Alice"}},
            "age":  {Kind: &pb.Value_NumberValue{NumberValue: 30}},
        },
    }

    // Deep copy
    dup := orig.CloneVT()

    // Change a nested value – original stays unchanged
    dup.Fields["name"].Kind = &pb.Value_StringValue{StringValue: "Bob"}
    fmt.Println(orig.Fields["name"].GetStringValue()) // Alice
    fmt.Println(dup.Fields["name"].GetStringValue()) // Bob
}

```

### Using the Generic CloneMessageVT Interface

For polymorphic code, use `CloneMessageVT()` which returns the `protobuf_go_lite.CloneMessage` interface. This is implemented in [`types/known/anypb/any.pb.go`](https://github.com/aperturerobotics/protobuf-go-lite/blob/main/types/known/anypb/any.pb.go) (around line 56):

```go
import (
    "fmt"
    "github.com/aperturerobotics/protobuf-go-lite"
    anypb "github.com/aperturerobotics/protobuf-go-lite/types/known/anypb"
)

func genericClone(msg protobuf_go_lite.Message) protobuf_go_lite.CloneMessage {
    // All generated messages implement CloneMessageVT()
    return msg.CloneMessageVT()
}

func demo() {
    src := &anypb.Any{TypeUrl: "type.googleapis.com/example.Foo", Value: []byte{0x01, 0x02}}
    cloned := genericClone(src).(*anypb.Any) // type-assert back to concrete type
    fmt.Printf("%+v\n", cloned)
}

```

## Summary

- **`CloneVT()`** generates a complete deep copy of any protobuf message, recursively cloning nested structures, maps, slices, and byte arrays.
- The generator code lives in **[`features/clone/clone.go`](https://github.com/aperturerobotics/protobuf-go-lite/blob/main/features/clone/clone.go)**, creating both `CloneVT()` (concrete return type) and `CloneMessageVT()` (generic interface) methods.
- Scalar fields copy by value, while reference types use `slices.Clone`, `maps.Clone`, or recursive `CloneVT` calls to ensure independence.
- The method safely handles `nil` receivers and preserves unknown fields during cloning.

## Frequently Asked Questions

### What is the difference between CloneVT and proto.Clone?

`CloneVT` is a statically generated method specific to `protobuf-go-lite` that returns a concrete type (`*MyMessage`), enabling compile-time type safety and avoiding reflection. Standard `proto.Clone` from `google.golang.org/protobuf/proto` uses reflection and returns the generic `proto.Message` interface, which requires type assertions and incurs runtime overhead.

### Does CloneVT handle nil receivers safely?

Yes. Every generated `CloneVT` implementation begins with a nil check: `if m == nil { return (*Message)(nil) }`. This ensures that calling `CloneVT()` on a nil pointer returns nil rather than causing a panic, making the method safe to use in chains or conditional logic.

### Are unknown fields preserved during cloning?

Yes. The generator explicitly clones the `unknownFields` byte slice using `slices.Clone(m.unknownFields)` when the slice is non-empty. This preserves any unrecognized protobuf wire-format data encountered during deserialization, ensuring that round-tripping through `CloneVT` does not lose information.

### Can I use CloneVT with generic code?

Yes, through the `CloneMessageVT()` method. While `CloneVT()` returns a concrete pointer type, `CloneMessageVT()` returns the `protobuf_go_lite.CloneMessage` interface. This allows you to write generic functions that can clone any message type without knowing the concrete type at compile time, though you may need to type-assert the result to access specific fields.