0

First of all: I really find it puzzling that my previous question gets closed and a hint pops up: "when to use drain vs into_iter" - that was not my question at all. So I repeat it here - with the hope that it will be read carefully:

I am struggling to learn the ownership concept! of Rust. On the one hand, I learned that each value has a single owner and that a vector owns its elements. Hence let v = vec![1,2,3]; let t = v[1] should invalidate the vector v. However if this is true, I struggle to understand the following excerpt from the book Programming Rust:

.. provide a drain method that takes a mutable reference to the collection and returns an iterator that passes ownership of each element to the consumer.

It then goes on to say

Unlike the into_iter() method, which takes the collection by value and consumes it, drain merely borrows a mutable ref to the collection.

In the example given:

let mut outer = "Earth".to_string();
let inner = String::from_iter(outer.drain(1..4));

assert_eq!(outer,"Eh");

one clearly sees that outer is still usable as a variable after the call of drain.

My question: Isn't this a contradiction? That is, if drain "returns an iterator that passes ownership of each element to the consumer", then we essentially have something like that the ownership of each element is passed to the String::from_iter function. This is analog to an assignment like let t=v[1], so I do not understand why I can still use outer afterwards if some elements of outer have been consumed in the function call.

EDIT: For a better explanation of my question: If something passes ownership of the elements of a string/vector. Then the vector/string is not anymore the owner. So how can it be used later on - it should be invalid to use it, because the ownership was moved.

EDIT2: Please read my question carefully: I do not want to know what drain does and when to use it. I just want to understand the ownership system better and therefore need an explanation for as to why there is no contradiction between the stated description and the use of outer after calling drain()

P.Jo
  • 532
  • 3
  • 9
  • 1
    `outer` is still usable but it no longer contains/owns drained elements - these were removed from it. – freakish Aug 24 '23 at 14:13
  • I know the result - but how is this possible given that "drain returns an iterator that passes ownership of each element to the consumer." If it passes ownership, the value outer should be invalid afterwards – P.Jo Aug 24 '23 at 14:17
  • 3
    Ownership of each element. Not ownership of `outer` itself. – freakish Aug 24 '23 at 14:18
  • 1
    As for `let t = v[1]`, this works only because ints are copyable. You get a copy in `t`. The compiler won't allow you to do that with non-copyable object. – freakish Aug 24 '23 at 14:19
  • In response to your latest edit(s), you are still confusing the ownership of the *container* with the ownership of *the things being contained*. If I have a cardboard box (string, vec, whatever) and it has two cats (ints, chars, whatever) in it, and I take out (drain) one cat, it's still a perfectly valid cardboard box, and there's still a perfectly valid cat inside it (owned by it) I just can't refer to the *second* cat in the box, because there isn't one anymore. If I take the *whole box* and give it to my neighbor (into_iter) *then* I can't refer to the box of cats in my house anymore. – Jared Smith Aug 24 '23 at 14:23
  • Please read the answers in the linked question carefully: _"`into_iter` consumes the collection itself, `drain` only consumes the values in the collection."_ `outer` is still pretty much usable and available after a call to `drain`. It is not contradictory, because whichever items were selected by `drain` were removed from the original string. – E_net4 Aug 24 '23 at 14:25
  • 1
    Perhaps you are confused about why doing `v[i]` is seemingly more restrictive than `drain`. That would be because it dereferences a single location of the vector or slice, which fails if the item type is not `Copy`. I will add https://stackoverflow.com/questions/27904864/what-does-cannot-move-out-of-index-of-mean to the list. – E_net4 Aug 24 '23 at 14:30
  • @jared_smith: Thanks -but it is still puzzling. According to Rust teminology every value has a single owner. So if I own an element in the container, then the container can't own its own element anymore. Or? – P.Jo Aug 24 '23 at 14:32
  • 1
    Exactly. Once you transfer ownership with drain, the container no longer owns that range of values. That's precisely the point. – freakish Aug 24 '23 at 14:33
  • @freakish: Yes, but doesn't a smart pointer like a vec (or similar a string) need to own a consecutive piece in memory? If it wouldn't own its second element for example, then there is suddenly a vector which has a hole in it (at least when considering the values it owns) – P.Jo Aug 24 '23 at 14:36
  • 1
    @P.Jo Indeed, `Vec` does require all items to be contiguous in memory. The elements not removed by `drain` are automatically rearranged internally so that they stay contiguous. – E_net4 Aug 24 '23 at 14:42
  • @P.Jo yes, so back to my catbox analogy the box "owns" the cats until I take one out, in which case it doesn't own that cat anymore but still owns the other cat in the box. But the box *itself* is also owned. If I give the box to my neighbor to adopt the cats, I can't take one out of the box anymore because I no longer have the box. Some function calls like `drain` take cats out of the box so the box no longer owns them but you can still refer to the box and the cats left in it. Some function calls take *ownership of the box itself*, in which case you can't refer to the box or the cats inside. – Jared Smith Aug 24 '23 at 15:11
  • ...cont'd as for the interior arrangement of memory to be contiguous, the data structure itself takes care of that for you, much like if you call `std::vector.erase` [in C++](https://cplusplus.com/reference/vector/vector/erase/). You don't have to re-shuffle the underlying array yourself. – Jared Smith Aug 24 '23 at 15:14
  • @Jared_smith: I think I get the analogy. But assume I want to write the function which takes out a specific cat - say v[2]. Assume a String lives at v[2] and I get ownership of that String via a variable s (now I have ownership of an element in the container v). Then when s is dropped the String to which v[2] is referring is dropped (because s was the owner). But this leaves v[2] invalid (in C terminology a dangling pointer) – P.Jo Aug 25 '23 at 09:16
  • 1
    @P.Jo As explained in [this answer](https://stackoverflow.com/a/27905403), you cannot move the string `v[2]` out of `v` into a new variable without also explicitly removing it from `v`, so nothing is left dangling. This is extending way beyond what is expected for a typical Stack Overflow question. You may benefit more from a venue that admits more back-and-forth, such as the [Rust Users discussion board](https://users.rust-lang.org), or maybe the [SO Rust chatroom](https://chat.stackoverflow.com/rooms/62927/rust). – E_net4 Aug 25 '23 at 10:15

0 Answers0