doufei3561 2019-03-02 06:41
浏览 9
已采纳

如何干净地处理结构状态错误?

So I'm just getting my feet wet with Go, and I'm trying to cleanly handle errors in the Go-way. One result is that having a type Movie struct with methods to update the record, and sync to a data store. So an example method:

func (movie Movie) SetTitle(title string) : error {
    prevTitle := movie.Title
    movie.Title = title
    json, err := json.Marshal(movie)
    if (err != nil) {
        movie.Title = prevTitle
        return err
    }

    err = db.SetValue(movie.id, json)
    if (err != nil) {
        movie.Title = prevTitle
        return err
    }

    return nil
}

The issue above is the 'cleanup' for a failed operation is to save the previous state, and restore as appropriate. But this feels very unmaintainable - it's so easy for a new error check to be made in the future, only to return without the proper cleanup. This would get even worse with multiple such 'cleanups'.

So to contrast, in other languages, I would wrap the whole bit in a try/catch, and handle cleanup just inside a single catch block. Here, I can:

  1. Create a copy of movie, do all the stuff on the copy, and then only copy back to the original object once everything is successful
  2. Change the logic to:
if json, err := json.Marshal(movie); err == nil {
  if err = db.SetValue(...); err == nil {
    return nil
  }
}

movie.Title = prevTitle;
return err

Which works, but I don't love having a level of nesting per check.

  1. Change the error return to indicate it was updated locally, but not saved
  2. Do it as described above
  3. Break out the save logic into an func Update() : err function to minimize the number of checks needed (just thought of this - think I like this one)

I feel like none of these are perfect; am I missing something obvious?

  • 写回答

2条回答 默认 最新

  • doumo0206 2019-03-02 07:10
    关注

    Update

    The way you are persisting can cause numerous problems:

    • you could incandescently mutate original object
    • you mix different layers in one method, it makes code very fragile
    • method, does too much

    I suggest, separate update and persistence. Playground

    type Movie struct {
        id    int
        Title string
    }
    
    func (m *Movie) Persist() error {
        json, err := json.Marshal(m)
        if err != nil {
            return err
        }
    
        log.Printf("Storing in db: %s", json)
    
        return nil
    }
    
    func main() {
        m := &Movie{1, "Matrix"}
        m.Title = "Iron Man"
    
        if err := m.Persist(); err != nil {
            log.Fatalln(err)
        }
    
    }
    

    Old answer

    In your example you use by-value receiver. In this case, you get a copy of the struct in the method, you free to modify that copy, but all changes will not be visible outside.

    func (movie Movie) SetTitleValueSemantic(title string) error {
        movie.Title = title
        json, err := json.Marshal(movie)
        if err != nil {
            return err
        }
    
        log.Printf("Serialized: %s", json)
    
        return nil
    }
    

    playgorund: https://play.golang.org/p/mVnQ66TCaG9


    I would strongly recommend avoiding such a coding style. In case you REALLY need some of this kind, here is an example for inspiration:

    playground: https://play.golang.org/p/rHacnsRLkEE

    func (movie *Movie) SetTitle(title string) (result *Movie, e error) {
        movieCopy := new(Movie)
        *movieCopy = *movie
        result = movie
    
        defer func() {
            if e != nil {
                result = movieCopy
            }
        }()
    
        movie.Title = title
        serialized, e := json.Marshal(movie)
        if e != nil {
            return
        }
    
        log.Printf("Serialized: %s", serialized)
    
        return
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 装 pytorch 的时候出了好多问题,遇到这种情况怎么处理?
  • ¥20 IOS游览器某宝手机网页版自动立即购买JavaScript脚本
  • ¥15 手机接入宽带网线,如何释放宽带全部速度
  • ¥30 关于#r语言#的问题:如何对R语言中mfgarch包中构建的garch-midas模型进行样本内长期波动率预测和样本外长期波动率预测
  • ¥15 ETLCloud 处理json多层级问题
  • ¥15 matlab中使用gurobi时报错
  • ¥15 这个主板怎么能扩出一两个sata口
  • ¥15 不是,这到底错哪儿了😭
  • ¥15 2020长安杯与连接网探
  • ¥15 关于#matlab#的问题:在模糊控制器中选出线路信息,在simulink中根据线路信息生成速度时间目标曲线(初速度为20m/s,15秒后减为0的速度时间图像)我想问线路信息是什么