V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
fumeboy

一个快速拷贝被类型转换为 interface 的 struct 的方法

  •  
  •   fumeboy · Dec 23, 2020 · 1836 views
    This topic created in 1954 days ago, the information mentioned may be changed or developed.

    方法是 从 interface 获取 struct 的地址, 然后将 struct 转成 []byte, 拷贝后再将 []byte 转成 struct

    package main_test
    
    import (
    	"fmt"
    	"reflect"
    	"testing"
    	"unsafe"
    )
    
    type beCopy struct {
    	value int
    }
    
    type emptyInterface struct {
    	typ  *struct{}
    	word unsafe.Pointer
    }
    type slice struct {
    	array unsafe.Pointer
    	len   int
    	cap   int
    }
    func Copy(v interface{}) interface{} {
    	// 传进来的是一个 struct, 但是类型转换为了 interface
    	// 目的是拷贝这个 struct
    	// 因为 interface 本质是一个携带了原来类型信息的指针
    	// 所以直接 值传递 拷贝是不行的, 值传递拷贝只能再得到一个这样的指针
    	var t = reflect.TypeOf(v)
    	var length = int(t.Size())
    
    	var vslice = []byte{}
    	// 所以这里将 struct 的地址替换到 slice 的地址位, 使 vslice 指向的一串内存就是 struct 的内存
    	(*slice)(unsafe.Pointer(&vslice)).array = (*emptyInterface)(unsafe.Pointer(&v)).word
    	(*slice)(unsafe.Pointer(&vslice)).len = length
    	(*slice)(unsafe.Pointer(&vslice)).cap = length
    
    	vvslice := make([]byte, length) // 再创建一个 slice
    	copy(vvslice, vslice) // 将 struct 的内存拷贝到新的 slice
    
    	vv := v // 拷贝一个 interface 指针
    	((*emptyInterface)(unsafe.Pointer(&vv))).word = (*slice)(unsafe.Pointer(&vvslice)).array // 将 新 slice 的内存地址 替换为结构体指针指向的地址
    	return vv // 返回深拷贝后的 interface 指针
    	// 大致意思就是,将 struct 转成 []byte, 拷贝后再将 []byte 转成 struct
    }
    
    func TestCopy(t *testing.T){
    	b := beCopy{value: 3}
    	d := Copy(b)
    
    	b.value++
    	e := Copy(b)
    	fmt.Println(b,d,e)
    }
    
    func BenchmarkCopy(b *testing.B) {
    	c := beCopy{value: 3}
    	for i := 0; i < b.N; i++ {
    		Copy(c)
    	}
    }
    
    Supplement 1  ·  Dec 24, 2020
    补充一下,这里的 reflect 是可以去掉的,可以用从 eface 的 type 结构体里获取结构体大小
    5 replies    2020-12-23 23:23:05 +08:00
    6ufq0VLZn0DDkL80
        1
    6ufq0VLZn0DDkL80  
       Dec 23, 2020   ❤️ 1
    这代码明显有 bug 。可能运气好没有挂掉。
    中间 copy 那一步,是没有带 write barrier 的。要搞也要用 typedmemmove 。
    fumeboy
        2
    fumeboy  
    OP
       Dec 23, 2020
    @cholerae 我自己也感觉会有 GC 的问题 .. typedmemmove 这些我去看一下
    wellsc
        3
    wellsc  
       Dec 23, 2020 via iPhone
    零抽象
    whitehack
        4
    whitehack  
       Dec 23, 2020
    只是浅拷贝吧?结构内的指针对象还是引用的同一个?
    lance6716
        5
    lance6716  
       Dec 23, 2020 via Android
    ValueOf 然后再 Copy ?
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   5282 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 47ms · UTC 07:08 · PVG 15:08 · LAX 00:08 · JFK 03:08
    ♥ Do have faith in what you're doing.