如何将具有相同结构的数据从一种类型复制到另一种类型?

huangapple go评论108阅读模式
英文:

How to copy data from one type to another that has identical structures?

问题

在下面的代码中:

type ProductEntity struct {
    ID          int     `json:"id"`
    Name        string  `json:"name"`
    Description string  `json:"description"`
    Price       float32 `json:"price"`
    SKU         string  `json:"sku"`
    CreatedOn   string  `json:"-"`
    UpdatedOn   string  `json:"-"`
    DeletedOn   string  `json:"-"`
}

type ProductEntityList []*ProductEntity

type PostRequestModel struct {
    ID          int     `json:"id"`
    Name        string  `json:"name"`
    Description string  `json:"description"`
    Price       float32 `json:"price"`
    SKU         string  `json:"sku"`
    CreatedOn   string  `json:"-"`
    UpdatedOn   string  `json:"-"`
    DeletedOn   string  `json:"-"`
}

type RequestBody []*PostRequestModel

func convertModelToEntity(modelList RequestBody) ProductEntityList {

    // return entity.ProductEntityList(modelList) // type conversion error 

}

如何将具有相同结构的数据从一个类型复制到另一个类型?因为RequestBodyProductEntityList是两个不同的类型定义。

英文:

In the below code:

type ProductEntity struct {
	ID          int     `json:"id"`
	Name        string  `json:"name"`
	Description string  `json:"description"`
	Price       float32 `json:"price"`
	SKU         string  `json:"sku"`
	CreatedOn   string  `json:"-"`
	UpdatedOn   string  `json:"-"`
	DeletedOn   string  `json:"-"`
}

type ProductEntityList []*ProductEntity

type PostRequestModel struct {
	ID          int     `json:"id"`
	Name        string  `json:"name"`
	Description string  `json:"description"`
	Price       float32 `json:"price"`
	SKU         string  `json:"sku"`
	CreatedOn   string  `json:"-"`
	UpdatedOn   string  `json:"-"`
	DeletedOn   string  `json:"-"`
}

type RequestBody []*PostRequestModel

func convertModelToEntity(modelList RequestBody) ProductEntityList {

	// return entity.ProductEntityList(modelList) // type conversion error 

}

How to copy data from one type to another, that has identical structures? Because RequestBody and ProductEntityList are two different type definitions

答案1

得分: 7

如果这些类型确实相等,可以使用type alias

type PostRequestModel = ProductEntity

如果这样做,你可以简单地将ProductEntityList转换为RequestBody(在Go Playground上尝试):

func convertModelToEntity(modelList RequestBody) ProductEntityList {
    return ProductEntityList(modelList) // Works!!
}

如果你不能使用类型别名,那么就无法将一个切片转换为另一个切片。你必须创建一个新的切片。请注意,你可以转换单个切片元素,因为指向的结构体类型具有相同的字段。这是可能的,因为规范:转换

非常量值x可以在以下任何情况下转换为类型T

  • [...]
  • 忽略结构体标签(见下文),x的类型和T都是指针类型,它们不是定义类型,并且它们的指针基础类型具有相同的底层类型。

因此,*ProductEntity可以转换为*PostRequestModel(反之亦然),因为ProductEntityPostRequestModel的底层类型是“相同的”结构体类型。

Go Playground上尝试:

func convertModelToEntity(modelList RequestBody) ProductEntityList {
    r := make(ProductEntityList, len(modelList))
    for i, m := range modelList {
        r[i] = (*ProductEntity)(m)
    }
    return r
}

还要注意,如果RequestBodyProductEntityList具有相同的内存布局(在你的示例中是这样的),你可以使用unsafe包来简单地转换它们,但我更愿意避免这样做(在Go Playground上尝试):

func convertModelToEntity(modelList RequestBody) ProductEntityList {
    return *(*ProductEntityList)(unsafe.Pointer(&modelList))
}

为什么要避免这样做?使用unsafe包可能会使你的应用程序不可移植,并且Go 1的兼容性保证可能不适用于它。例如,你可能只向ProductEntity添加一个字段,而不是PostRequestModel。结果,你的应用程序将继续编译而没有错误,但可能随时崩溃。始终将unsafe包视为最后的手段。

英文:

If those type are truly equal, use type alias:

type PostRequestModel = ProductEntity

If you do so, you can simply convert from ProductEntityList to RequestBody (try it on the Go Playground):

func convertModelToEntity(modelList RequestBody) ProductEntityList {
    return ProductEntityList(modelList) // Works!!
}

If you can't use type alias, then you can't convert from one slice to another. You have to create a new slice. Note that you can convert the individual slice elements because the pointed struct types have identical fields. This is possible because Spec: Conversions:

> A non-constant value x can be converted to type T in any of these cases:
> - [...]
> - ignoring struct tags (see below), x's type and T are pointer types that are not defined types, and their pointer base types have identical underlying types.

So *ProductEntity is convertible to *PostRequestModel (and vice versa) because the underlying type of ProductEntity and PostRequestModel is the "same" struct type.

Try it on the Go Playground:

func convertModelToEntity(modelList RequestBody) ProductEntityList {
	r := make(ProductEntityList, len(modelList))
	for i, m := range modelList {
		r[i] = (*ProductEntity)(m)
	}
	return r
}

Also note that if RequestBody and ProductEntityList have identical memory layout (they do in your example), you may use package unsafe to simply convert them, but I'd rather avoid it (try it on the Go Playground):

func convertModelToEntity(modelList RequestBody) ProductEntityList {
	return *(*ProductEntityList)(unsafe.Pointer(&modelList))
}

Why to avoid this? Using package unsafe your app may become non-portable and the Go 1 compatibility guarantees may not apply to it. E.g. you may add a field to ProductEntity only but not to PostRequestModel. As a result, your app will continue to compile without errors, but may blow up at any time. Always look at package unsafe as the last resort.

huangapple
  • 本文由 发表于 2021年5月31日 00:08:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/67763249.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定