rust 为什么我可以用while循环将项目推入一个Vec中,而不能用for循环?

yrwegjxp  于 11个月前  发布在  其他
关注(0)|答案(3)|浏览(130)

就像下面的rust代码:while循环编译并运行良好,但for iter版本由于错误而无法编译:

error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
  --> src/main.rs:22:9
   |
20 |     for i in v.iter() {
   |              --------
   |              |
   |              immutable borrow occurs here
   |              immutable borrow later used here
21 |         println!("v[i]: {}", i);
22 |         v.push(20);
   |         ^^^^^^^^^^ mutable borrow occurs here

error: aborting due to previous error

字符串
但据了解,while循环也有相同的情况,lenget也借用不变,为什么它不冲突与push作为借用可变?请告诉我,我的理解错过了这里,非常感谢的启发!

fn main() {
    let mut v = Vec::new();

    v.push(1);
    v.push(2);
    v.push(3);
    v.push(4);

    let mut i = 0;

    while i < v.len() && i < 10 {
        v.push(20);
        println!("v[i]: {:?}", v.get(i));
        i += 1;
    }

    // for i in v.iter() {
    //     println!("v[i]: {}", i);
    //     v.push(20);
    // }
}

jtjikinw

jtjikinw1#

代码的for版本大致相当于以下内容:

fn main() {
    let mut v = Vec::new();
    v.push(1);
    v.push(2);
    v.push(3);
    v.push(4);
    
    let mut it = v.iter();
    while let Some(i) = it.next() {
        println!("v[i]: {}", i);
        v.push(20);
    }
}

字符串
Playground
如果你试图编译它,你会得到一个错误,也许更有意义一点:

error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
  --> src/main.rs:11:9
   |
8  |     let mut it = v.iter();
   |                  - immutable borrow occurs here
9  |     while let Some(i) = it.next() {
   |                         -- immutable borrow later used here
10 |         println!("v[i]: {}", i);
11 |         v.push(20);
   |         ^^^^^^^^^^ mutable borrow occurs here


迭代器在整个循环期间不可变地借用v,因此在循环内不能进行任何可变借用。
当然,即使你能做到这一点,你最终会得到一个无限循环,因为你不断追加另一个项目。

w80xi6nr

w80xi6nr2#

简单地说,当您调用.iter()时,您将创建一个新对象(一个迭代器),它借用你的向量(不可变),并且the一个接一个地给出元素,这意味着你实际上借用了v整个循环时间。另一方面,当你通过.get(i)访问它时,你直接从vector中借用一个元素,所以当你push的时候它就不受借用的限制了。
这种限制的原因很简单:假设实际的for循环编译了,它将永远运行。(为了防止在while循环中出现这种情况,您必须添加人工条件i<10!),而这显然不是预期的目标(或者如果是的话,你显然会做其他的事情,例如使用while letloop语句),Rust试图防止你“开枪打自己的腿”,因为你真的不知道如何做你想做的事情,并尝试错误的方式。
要做你想做的事,你可以做:

for i in 0..v.len() {
    v.push(20)
}

字符串
因为v.len()调用在整个for循环的时间内不借用v,而是仅在开始时借用。

lmyy7pcs

lmyy7pcs3#

Rust borrow checker定义了一组规则,以确定可以借用哪些模式(可变或不可变)。它甚至做得更多,即处理生命周期,如果你不借用,你就移动。
对于有抱负的Rust程序员来说,处理借用检查器强加的规则是痛苦之谷(又名学习曲线),他们必须通过才能变得富有成效。
一旦迭代器不可变地借用了向量(并在循环期间保持它),借用检查器就会抱怨尝试变异。
如果你是一个横向思考者,就像我一样......每当我看到一种新语言的东西,我不能完全理解我的头,我试着用一种我已经更了解的语言写一个教育性的近似“奇怪的东西”。
所以,希望它能有所帮助,你可以找到一个非常简化和大胆的近似rust在C中的行为(在编译时)。只是-在我的C代码中,它发生在运行时,违反规则会导致抛出异常。
我们使用的示例的“状态”(在本例中为std::vector<int32_t>),关于可变性以及根据我们匆忙发明的借用规则集我们仍然可以做什么(可能与 rust 病相似,也可能不相似)由不同的类型表达(Managed<T>::ConstRefManaged<T>::MutableRef)。状态应用的范围是lambda函数的范围,下面代码中的main()试图复制for i in vec.iter() { .. }场景。
也许从这个Angular 看问题对某人有帮助。

#include <iostream>
#include <cstdint>
#include <vector>
#include <string>
#include <stdexcept>
#include <sstream>

template<class T>
void notNull(const T* p)
{
  if(nullptr == p)
    throw std::invalid_argument("pointer is null!");
};

enum class Demand : uint8_t
{ CONST
, MUTABLE
};

inline
std::string
exinfo
(const char* message
, const char * file
, int line
 )
{
  std::ostringstream os;
  os << file << ":" << line << std::endl
     << message << std::endl;
  return os.str();
}

class borrow_error
  : public std::logic_error
{
public:
  explicit borrow_error(const std::string& what_arg)
    : logic_error(what_arg)
  {}
  explicit borrow_error(const char* what_arg)
    : logic_error(what_arg)
  {}
};
class mutable_borrow_after_const_borrow
  : public borrow_error
{
public:
  mutable_borrow_after_const_borrow(const char* file, int line)
    : borrow_error
      (exinfo("mutable borrow after const borrow",file,line))
  {}
};

#define THROW_MUTABLE_BORROW_AFTER_CONST_BORROW \
  throw mutable_borrow_after_const_borrow(__FILE__,__LINE__)

class const_borrow_after_mutable_borrow
  :public borrow_error
{
public:
  const_borrow_after_mutable_borrow(const char* file, int line)
    : borrow_error
      (exinfo("const borrow after mutable borrow",file,line))
  {}
};

#define THROW_CONST_BORROW_AFTER_MUTABLE_BORROW \
  throw const_borrow_after_mutable_borrow(__FILE__,__LINE__)

class multiple_mutable_borrows
  :public borrow_error
{
public:
  multiple_mutable_borrows(const char* file, int line)
    : borrow_error
      (exinfo("more than one mutable borrow",file,line))
  {}
};

#define THROW_MULTIPLE_MUTABLE_BORROWS \
  throw multiple_mutable_borrows(__FILE__,__LINE__)

class mutable_access_to_const_attempt
  : public borrow_error
{
public:
  mutable_access_to_const_attempt(const char* file, int line)
    : borrow_error
      (exinfo("request to mutate the immutable.",file,line))
  {}
};

#define THROW_MUTABLE_ACCESS_TO_CONST_ATTEMPT \
  throw  mutable_access_to_const_attempt(__FILE__,__LINE__)

template <class T>
struct Managed
{
  using this_type = Managed<T>;
  using managed_type = T;
  struct ConstRef
  {
    this_type * origin;
    ConstRef(this_type *org, const char* file, int line)
      : origin{org}
    {
      notNull(origin);
      // if( 0 != origin->mutableAccess )
      // {
      //    throw const_borrow_after_mutable_borrow(file,line);
      // }
      origin->constAccess++;
    }
    ~ConstRef()
    {
      origin->constAccess--;
    }
    operator const T&()
    {
      return origin->instance;
    }
  };
  struct MutableRef
  {
    this_type *origin;
    MutableRef(this_type *org, const char* file, int line)
      : origin{org}
    {
      notNull(origin);
      if(origin->instanceMode == Demand::CONST)
      {
    throw mutable_access_to_const_attempt(file,line);
      }
      if( 0 != origin->constAccess )
      {
    throw mutable_borrow_after_const_borrow(file,line);
      }
      // also allow max 1 mutator
      if( 0 != origin->mutableAccess )
      {
    throw multiple_mutable_borrows(file,line);
      }
      origin->mutableAccess++;
    }
    ~MutableRef()
    {
      origin->mutableAccess--;
    }
    operator T&()
    {
      return origin->instance;
    }
  };
  
  Demand instanceMode;
  int32_t constAccess;
  int32_t mutableAccess;
  T& instance;
  Managed(T& inst, Demand demand = Demand::CONST)
    : instanceMode{demand}
    , constAccess{0}
    , mutableAccess{0}
    , instance{inst}
  {
  }
};

template <typename T, class F>
auto
borrow_const
( T& instance
, F body
, const char * file
, int line
) -> void
{
  typename T::ConstRef arg{&instance, file, line};
  body(arg);
}

#define BORROW_CONST(inst,body) \
  borrow_const((inst),(body),__FILE__,__LINE__)

template <typename T, class F>
auto
borrow_mut
( T& instance
, F body
, const char * file
, int line
) -> void
{
  typename T::MutableRef arg{&instance, file, line};
  body(arg);
};

#define BORROW_MUT(inst,body) \
  borrow_mut((inst),(body),__FILE__,__LINE__)

using VecI32 = std::vector<int32_t>;
using ManagedVector = Managed<VecI32>;

int main(int argc, const char *argv[])
{
  VecI32 myVector;
  ManagedVector vec{myVector,Demand::MUTABLE};

  try
  {
    BORROW_MUT
      ( vec
      , [] (ManagedVector::MutableRef& vecRef) -> void
    {
      static_cast<VecI32&>(vecRef).push_back(1);
      static_cast<VecI32&>(vecRef).push_back(2);
      // during iteration, changing targets are bad...
      // if you borrow the vector to an iterator,
      // the "state" of the vector becomes 'immutable'.
      BORROW_CONST
        ( *(vecRef.origin)
        , [] (typename ManagedVector::ConstRef& vecRef) -> void
          {
        for( auto i : static_cast<const VecI32&>(vecRef) )
        {
          std::cout << i << std::endl;
          // Enforced by the rust compiler established
          // borrow rules,
          // it is not allowed to borrow mut from something
          // immutable.
          // Thus, trying to change what we iterate over...
          // should be prevented by the borrow checker.
          BORROW_MUT
            ( *(vecRef.origin)
            , [] (typename ManagedVector::MutableRef& vecRef)
                 -> void
              {
            // next line should throw!
            // but our BORROW_MUT throws first.
            static_cast<VecI32&>(vecRef).push_back(3); 
              });
        }
          });
    });
  }
  catch(borrow_error& berr)
  {
    std::cout << "borrow error: " << berr.what() << std::endl;
  }
  catch(std::logic_error& lerr)
  {
    std::cout << "logic error: " << lerr.what() << std::endl;
  }
  catch(std::exception& ex)
  {
    std::cout << "std::exception: " << ex.what() << std::endl;
  }
  catch(...)
  {
    std::cout << "We are taking horrible, HORRIBLE damage!" << std::endl;
  }
  
  return 0;
}

字符串

相关问题