关于rust更精细的所有权move问题,未来能否有一个RFC支持partial move

struct Point{
  x: i32,
  y: i32
}
impl Point{
  fn move_x(self){
    self.x
  }
  fn move_x_rfc(self::M)
  where M: self::x
  {
     self.x
  }
  fn move_y(self){
    self.y
  }
  fn move_y_rfc(self::M)
   where M:: self.y
  {
     self.y
  }
}
// 更复杂的嵌套类型
struct Inner{
  f1: String,
  f2: String
}
struct Data{
  x: i32,
  inner: Inner
}
impl Data{
  fn move_x_inner_f1(self::M) -> (i32, String)
  where M:: self.x + self::inner::f1
  {
    (self.x, self.inner.f1)
  }
  fn move_inner_f2(self::M) -> String
   where M: self::inner::f2
  {
    self.inner.f2
  }
}
fn test(){
  let pt = Point::new(3, 4);
  let xx = pt.move_x(); // ok
  let yy = pt.move_y(); // error, 使用一个已经move的point

  // 更复杂的path pattern partial move
  let data = Data::new(10, Inner::new("aa".to_string(), "bb".to_string()));
  let _ = data.move_x_inner_f1();
  let _ = data.move_inner_f2();
}
2 个赞

看不懂 顶一下

M 是啥:thinking:

M是一种新的syntax, 标记struct的哪些field在该方法调用后被move, 而fn move(self)这样的signature会使得整个struct都被编译器标记为moved

加了这个语法能解决什么问题呢?或者说现在有怎样的需求,但现有的语法不能很好的满足。
因为现在移动的场景完全可以再写几个PointXPointY组合一下实现。

1 个赞

没明白你是要解决什么问题。如果只是想move x 的话self 传引用就行啊。

impl Point {
    fn move_x(&self) -> i32 {
        self.x
    }
    fn move_y(self) -> i32 {
        self.y
    }
}

感觉没有必要加

建议去 Rust internals 提, 没遇到过这种需求, 不做评论.

如果是move_x(self)这种签名,那调用move_x后,整个对象都被标记为move, 而事实上self.y还拥有所有权

加了以后可以在调用move_x以后只做partial move标记,只有point.x被标记为move, 继续访问point的其他属性是被允许的,move的颗粒度更加精细。现在语法,如果x, y都是public, 你可以在栈空间调用xx = point.x, yy = point.y分离所有权,而如果从方法调,方法签名是fn move_x(self), 整个self都被标记为move了,再次调用move_y是不能通过编译的。

为什么没有必要,我看rust rfcs中有对应的partial borrow, partial borrow中在trait bound中通过特殊的生命周期约束标记只借用对象的一个field而不是整个对象, 那么partial move应该也是合理的。

这种需求直接 Option::take 不就得了, 传 &mut self, 后续调用判空就行. 添加这个部分 move 带来的复杂度不小, 语法也要改, 如非必要勿增实体.

需要指出, 前面一位佬友说的 move_x, 本质是 i32 实现了 Copy, 自动复制了一份, 不是 move.

Option::take当然是可以实现类似的需求,但是类型系统变得太复杂了,并没有从根本上解决问题。从问题本身来看,是rust的move和借用逻辑在方法签名上的设计缺陷,导致整个所有权系统颗粒度过大,不方便做精细的所有权控制。官方的rfcs里面也有partial borrow的,通过field 特殊生命周期,标记partial borrow的field, 从而实现精细的borrow控制。官方的partial borrow目前还是open状态,如果partial borrow可以实现,partial move的存在也是合理的。

说实在的, 不像 partial borrow 是经常见到的情况, 给个实际项目的案例, 要不然也说不明白这个所谓 partial move 的提案必要性在哪.

要么, 不需要这个 struct 的整体了, 这种情况完全可以直接拆了这个 struct, 对各部分分开处理;

要么就 Option, take 掉, None 表示已经处理了, 常见于为一个 struct 实现 Future 的情况. C 里面判 null 也是一样道理.

这种 Partial move 只是解决传 self 但只需要 move 其中一部分的情况, 很罕见, 不引入新的标识符, 就是 breaking change, 依赖传 self 并离开作用域后即被 Drop, 引入 partial move 就说不清了, 后面还是可能意外调用, 是灾难性的, 所以不可能像 partial borrow 一样无需改代码编译器层面实现; 而引入新标识就不是件简单的事情了, 牵一发动全身, 为了解决这极罕见的需求, 我认为是完全没必要的.

总之, 我认为这 RFC 没有多大意义, 但我不是开发 Rust 语言本身的开发者, 你觉得你这个 RFC 蛮有价值可以去官方社区提, 会有大佬给出详细回复的.

我理解你的意思,要实现这个feature,目前的语法肯定会breaking change。但是我不认为这是个罕见的需求,比如一个我加载了一个复杂的DataClass,需要对他的每个field move并通过链式调用预处理,就需要分离所有权了,如果直接split_fields, 然后分别处理当然也是可以的,但是就不能优雅的使用链式调用了。使用Option包一层是个折中的方案,但是这个DataClass变得更复杂了,在业务上Option带来了不小的语法噪音。

1 个赞

谢谢你的回复,只是个人的一点想法,毕竟rust还在不断发展中,还有很多rfcs在开发中,所以提出来讨论一下。

此话题已在最后回复的 30 天后被自动关闭。不再允许新回复。