V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
fumeboy
V2EX  ›  Go 编程语言

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

  •  
  •   fumeboy · 2020-12-23 12:36:29 +08:00 · 1365 次点击
    这是一个创建于 1212 天前的主题,其中的信息可能已经有所发展或是发生改变。

    方法是 从 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)
    	}
    }
    
    第 1 条附言  ·  2020-12-24 09:53:59 +08:00
    补充一下,这里的 reflect 是可以去掉的,可以用从 eface 的 type 结构体里获取结构体大小
    5 条回复    2020-12-23 23:23:05 +08:00
    cholerae
        1
    cholerae  
       2020-12-23 12:52:56 +08:00   ❤️ 1
    这代码明显有 bug 。可能运气好没有挂掉。
    中间 copy 那一步,是没有带 write barrier 的。要搞也要用 typedmemmove 。
    fumeboy
        2
    fumeboy  
    OP
       2020-12-23 12:58:53 +08:00
    @cholerae 我自己也感觉会有 GC 的问题 .. typedmemmove 这些我去看一下
    wellsc
        3
    wellsc  
       2020-12-23 13:01:13 +08:00 via iPhone
    零抽象
    whitehack
        4
    whitehack  
       2020-12-23 18:08:04 +08:00
    只是浅拷贝吧?结构内的指针对象还是引用的同一个?
    lance6716
        5
    lance6716  
       2020-12-23 23:23:05 +08:00 via Android
    ValueOf 然后再 Copy ?
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1205 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 23:18 · PVG 07:18 · LAX 16:18 · JFK 19:18
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.