前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Github Copilot:开发者的动次打次切克闹

Github Copilot:开发者的动次打次切克闹

作者头像
tyrchen
发布2022-03-29 10:14:58
7230
发布2022-03-29 10:14:58
举报
文章被收录于专栏:程序人生程序人生

作为一个 TabNine 的资深免费用户,在遇到 Github Copilot 的第一晚,我就无可救药地爱上了后者,并义无反顾地卸载了前者。作为 AI coding 时代的杰出代表,Github Copilot 做到了比我自己还更懂我。以前,写代码的时候,我基本都是气沉丹田紧锁眉头沉默不语;自从用上了 Copilot,画风就变成了:艾玛,我靠,我去,真的假的,不可能吧。

药,药,切克闹。

比如下面的代码(双下划线内部代码为自动补齐代码):

代码语言:javascript
复制
#[derive(Debug)]
struct State {
    // for a given user, how many rooms they're in
    user_rooms: DashMap<String, DashSet<String>>,
    // for __a given room, how many users are in it__
    __room_users: DashMap<String, DashSet<String>>__,
    tx: broadcast::Sender<Arc<Msg>>,
}

当我写完 user_rooms 的定义后,换行,打出注释 // for,copilot 能够自动提示可以补齐后续的注释,当我 tab 使用这个补齐后,紧接着它有帮我补齐 room_users 那一行。

在我进一步实现 get_user_rooms 这个方法时,写完方法名,就得到了完整定义和实现的自动补齐提示:

代码语言:javascript
复制
#[derive(Debug, Clone, Default)]
pub struct ChatState(Arc<State>);

impl ChatState {
    pub fn new() -> Self {
        Self(Default::default())
    }

    pub fn get_user_rooms__(&self, username: &str) -> Option<&DashSet<String>> {
        self.0.user_rooms.get(username)
    }__

这个方法的参数是我期待的,但返回值并非我所想要的。于是我把补全的代码稍作修改。随后继续写 下一个方法 get_room_users,有了上个方法的铺垫,这次 copilot 完全正确地补全了我的意图:

代码语言:javascript
复制
impl ChatState {
    pub fn new() -> Self {
        Self(Default::default())
    }

    pub fn get_user_rooms(&self, username: &str) -> Vec<String> {
        self.0
            .user_rooms
            .get(username)
            .map(|rooms| rooms.clone().into_iter().collect())
            .unwrap_or_default()
    }

    pub fn get_room_users__(&self, room: &str) -> Vec<String> {
        self.0
            .room_users
            .get(room)
            .map(|users| users.clone().into_iter().collect())
            .unwrap_or_default()
    }__
}

当然,copilot 让人感觉惊艳的同时,也还有不小的瑕疵:代码可能并不能正确编译,有时甚至能够编译通过但有逻辑 bug。比如我定义了如下消息类型:

代码语言:javascript
复制
#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum MsgData {
    Join,
    Leave,
    Message(String),
}

在对其进行 match 的过程中,copilot 猜出了我主要的意图,并提供了如下的代码补全:

代码语言:javascript
复制
match msg.data {
    MsgData::Join => {
        state
            .user_rooms
            .entry(msg.username)
            .or_insert_with(DashSet::new)
            .insert(msg.room);
        state
            .room_users
            .entry(msg.room) // 这里有 ownership 问题
            .or_insert_with(DashSet::new)
            .insert(msg.username); // 这里有 ownership 问题
    }
    MsgData::Leave => {
        if let Some(v) = state.user_rooms.get_mut(&msg.username) {
            v.remove(&msg.room);
            if v.is_empty() {
                // 这里有死锁问题
                state.user_rooms.remove(&msg.username);
            }
        }
        if let Some(v) = state.room_users.get_mut(&msg.room) {
            v.remove(&msg.username);
            if v.is_empty() {
                // 这里有死锁问题
                state.room_users.remove(&msg.room);
            }
        }
    }
    ...
};

其中,一开始处理 MsgData::Leave 的代码结构和 MsgData::Join 的类似:

代码语言:javascript
复制
MsgData::Join => {
    state
        .user_rooms
        .entry(msg.username)
        .or_insert_with(DashSet::new)
        .remove(msg.room);
    state
        .room_users
        .entry(msg.room)
        .or_insert_with(DashSet::new)
        .remove(msg.username);
    msg
}

但当我将其修改成这一句

代码语言:javascript
复制
if let Some(v) = state.user_rooms.get_mut(&msg.username) {

后,copilot 给我了不一样的补全方案(如上),预判了我的预判。

如果我们看上面的完整的补全代码,可以看到,撰写出这样的代码可能要几分钟时间。这是 copilot 能够为我省下的时间。不过,代码中有几处注释中提到的错误,尤其那个死锁的问题:

代码语言:javascript
复制
if let Some(v) = state.user_rooms.get_mut(&msg.username) {
    v.remove(&msg.room);
    if v.is_empty() {
        // 这里有死锁问题,需要先 drop(v)
        state.user_rooms.remove(&msg.username);
    }
}

还额外花了我几分钟时间才揪出来(我在 unit testing 时发现的,否则花费时间更长)。不过,扪心自问的话,我自己撰写相同的代码,可能也很难第一时间意识到这里需要 drop(v)

如何用好 copilot?

在过去使用 copilot 的一个来月,我感觉有几点特别重要:

  1. 先定义好Trait,数据结构,再去做实现。copilot 会根据你已有代码的各种信息推测你接下来会做什么;
  2. 多用容易理解的名字,帮助 copilot 理解你的代码;
  3. 函数要尽可能单一职责,并且写的简短;
  4. 重复 pattern 的代码,不要着急写,给 copilot 一点点时间,它会帮你很好地补全。

虽然目前 copilot 对各种语言都有不错的支持,不过我建议在动态类型语言下,如 Python,JavaScript 要特别小心自动生成的代码,因为即便 copilot 提供了错误的代码(比如访问一个对象下并不存在的域),这些语言也不会报错,可能会把很多错误的发现推迟至运行时。

AI Coding 对程序员是福是祸?

目前我使用的感受是,AI coding 可以使程序员变得更加高效,尤其是优秀的程序员。它会进一步拉开设计良好,命名良好的系统和设计并不好的系统的开发效率,也会拉开优秀程序员和普通程序员的效率。我个人感觉,虽然大家的效率都提高了,但优秀开发者的效率提升会更大。原因有二:1. 优秀开发者的思路更清晰,设计和代码风格更好,AI 可以更容易「猜测」出来作者的意图;2. 优秀开发者阅读和理解 AI 给出的代码建议会更快,会更快地决定是否采用(以及采用后修改)推荐的代码。

因为代码产出的速率提升,同样的工作原本需要十个人月,使用了 AI Coding 后,可能八个人月就能完成。所以,我觉得在某种程度上,未来 AI coding 可能会减少一些对程序员的需求。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-03-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序人生 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 如何用好 copilot?
  • AI Coding 对程序员是福是祸?
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档