String concatenation from vector tuple - rust

This is as simple as it gets, yet I have no clue why it doesn't work.
fn main() {
let vector = vec![("foo".to_string(), "bar".to_string())];
let string = vector[0].0 + vector[0].1;
}
Error
src/main.rs:3:29: 3:40 error: mismatched types:
expected `&str`,
found `collections::string::String`
(expected &-ptr,
found struct `collections::string::String`) [E0308]
src/main.rs:3 let string = vector[0].0 + vector[0].1;
^~~~~~~~~~~
So then I change it to this:
fn main() {
let vector = vec![("foo".to_string(), "bar".to_string())];
let string = &*vector[0].0 + &*vector[0].1;
}
Get another error
src/main.rs:3:15: 3:28 error: binary operation `+` cannot be applied to type `&str` [E0369]
src/main.rs:3 let string = &*vector[0].0 + &*vector[0].1;
^~~~~~~~~~~~~
src/main.rs:3:15: 3:28 help: run `rustc --explain E0369` to see a detailed explanation
src/main.rs:3:15: 3:28 note: an implementation of `std::ops::Add` might be missing for `&str`
src/main.rs:3 let string = &*vector[0].0 + &*vector[0].1;
^~~~~~~~~~~~~
I've exhausted all the combinations I could think of. What am I missing here?

This does not work because concatenation is defined only on String, and it consumes its left operand:
let s = "hello ".to_string();
let c = s + "world";
println!("{}", c); // hello world
println!("{}", s); // compilation error
Therefore it needs by-value access to the string, but it cannot be done with indexing on a vector - they can only return references into the vector, not values.
There are several ways to overcome this, for example, you can clone the string:
let string = vector[0].0.clone() + &vector[0].1;
Or you can use formatting:
let string = format!("{}{}", vector[0].0, vector[0].1);
Or you can take the value out of the vector with remove() or swap_remove():
let string = match vector.swap_remove(0) {
(left, right) => left + right
};
The latter, naturally, is appropriate if it's okay for you to lose the state of the vector. If you want to do this for the whole vector, it is better to iterate it by value, consuming it in the process:
for (left, right) in vector {
let string = left + right;
}

Related

Why does size for value types matter when returning a value vs a reference? [duplicate]

I'm trying to manipulate a string derived from a function parameter and then return the result of that manipulation:
fn main() {
let a: [u8; 3] = [0, 1, 2];
for i in a.iter() {
println!("{}", choose("abc", *i));
}
}
fn choose(s: &str, pad: u8) -> String {
let c = match pad {
0 => ["000000000000000", s].join("")[s.len()..],
1 => [s, "000000000000000"].join("")[..16],
_ => ["00", s, "0000000000000"].join("")[..16],
};
c.to_string()
}
On building, I get this error:
error[E0277]: the trait bound `str: std::marker::Sized` is not satisfied
--> src\main.rs:9:9
|
9 | let c = match pad {
| ^ `str` does not have a constant size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `str`
= note: all local variables must have a statically known size
What's wrong here, and what's the simplest way to fix it?
TL;DR Don't use str, use &str. The reference is important.
The issue can be simplified to this:
fn main() {
let demo = "demo"[..];
}
You are attempting to slice a &str (but the same would happen for a String, &[T], Vec<T>, etc.), but have not taken a reference to the result. This means that the type of demo would be str. To fix it, add an &:
let demo = &"demo"[..];
In your broader example, you are also running into the fact that you are creating an allocated String inside of the match statement (via join) and then attempting to return a reference to it. This is disallowed because the String will be dropped at the end of the match, invalidating any references. In another language, this could lead to memory unsafety.
One potential fix is to store the created String for the duration of the function, preventing its deallocation until after the new string is created:
fn choose(s: &str, pad: u8) -> String {
let tmp;
match pad {
0 => {
tmp = ["000000000000000", s].join("");
&tmp[s.len()..]
}
1 => {
tmp = [s, "000000000000000"].join("");
&tmp[..16]
}
_ => {
tmp = ["00", s, "0000000000000"].join("");
&tmp[..16]
}
}.to_string()
}
Editorially, there's probably more efficient ways of writing this function. The formatting machinery has options for padding strings. You might even be able to just truncate the string returned from join without creating a new one.
What it means is harder to explain succinctly. Rust has a number of types that are unsized. The most prevalent ones are str and [T]. Contrast these types to how you normally see them used: &str or &[T]. You might even see them as Box<str> or Arc<[T]>. The commonality is that they are always used behind a reference of some kind.
Because these types don't have a size, they cannot be stored in a variable on the stack — the compiler wouldn't know how much stack space to reserve for them! That's the essence of the error message.
See also:
What is the return type of the indexing operation?
Return local String as a slice (&str)
Why your first FizzBuzz implementation may not work

No method named `push_str` found for type `&str` after trimming a string

I keep getting this error. I'm assuming it's because I shadow answer trimming it since when I comment that part out I don't get the error anymore. I don't understand why that is.
fn main() {
let mut answer = String::new();
let num = 40;
if num % 2 == 0 {
answer.push_str("fact2 ");
}
if num % 5 == 0 {
answer.push_str("fact5 ");
}
let answer = answer.trim();
answer.push_str("bob was here");
println!("{}", answer);
}
error[E0599]: no method named `push_str` found for type `&str` in the current scope
--> src/main.rs:13:12
|
13 | answer.push_str("bob was here");
| ^^^^^^^^
I'm assuming it's because I shadow answer trimming it
Yes. String::trim returns a &str:
pub fn trim(&self) -> &str
&str does not have the push_str method.
See also:
Why does `name = *name.trim();` give me `expected struct `std::string::String`, found str`?
Efficient trimming of a String
How to "crop" characters off the beginning of a string in Rust?
You're right, let answer = answer.trim(); is the problem. It sets answer to have type &str, and that push_str is defined for a mutable String.
You can fix it by changing that line to:
answer = answer.trim().to_string();

How does the match expression interpret values vs. references?

In this example, why can I dereference t in the match expression but not on the line directly above?
fn tree_weight_v2(t: &BinaryTree) -> i32 {
// let x = *t; // if uncommented, error: "Cannot move out of borrowed content"
match *t {
BinaryTree::Leaf(payload) => payload,
BinaryTree::Node(ref left, payload, ref right) => {
tree_weight_v2(left) + payload + tree_weight_v2(right)
}
}
}
#[test]
fn tree_demo_2() {
let tree = sample_tree();
assert_eq!(tree_weight_v2(&tree), (1 + 2 + 3) + 4 + 5);
assert_eq!(tree_weight_v2(&tree), (1 + 2 + 3) + 4 + 5);
// no error ^ meaning tree_weight_v2 is not taking ownership of tree
}
enum BinaryTree {
Leaf(i32),
Node(Box<BinaryTree>, i32, Box<BinaryTree>)
}
fn sample_tree() -> BinaryTree {
let l1 = Box::new(BinaryTree::Leaf(1));
let l3 = Box::new(BinaryTree::Leaf(3));
let n2 = Box::new(BinaryTree::Node(l1, 2, l3));
let l5 = Box::new(BinaryTree::Leaf(5));
BinaryTree::Node(n2, 4, l5)
}
playground
I don't believe what the code is doing beyond the match statement to be important - unless of course, that is the source of my confusion.
I'm also curious about how match expressions handle dereferenced values. Specifically, since what the match expression sees is a value of type BinaryTree (without any references), why does the match expression not try to take ownership over it? More generally, how does Rust's match interpret the difference between a dereferenced pointer to a value and a normal value?
let x = *t does not work, because it's moved out of the reference. I.e. this means, with dereferencing t, you get a BinaryTree (without reference). The BinaryTree is borrowed in the function, so assigning it with let x = *t will move the it into the x, which is not possible, since it's borrowed.
The match does work, because match borrows the variable. match *t will not move it out, but it will borrow the BinaryTree. This is a special syntax in Rust and the borrow happens behind the scenes. You can think of this:
fn tree_weight_v2(t: &BinaryTree) -> i32 {
match &*t {
&BinaryTree::Leaf(payload) => payload,
&BinaryTree::Node(ref left, payload, ref right) => {
tree_weight_v2(left) + payload + tree_weight_v2(right)
}
}
}
but all the references are not needed.
See also:
Is there any difference between matching on a reference to a pattern or a dereferenced value?
Should a reference to an enum be dereferenced before it is matched?

`expected reference, found integral variable` in accessing a value from BTreeMap

I'm trying to retrieve a key from a BTreeMap and manipulate it in subsequent code.
use std::collections::BTreeMap;
fn main() {
let mut map: BTreeMap<u64, u64> = BTreeMap::new();
map.entry(0).or_insert(0);
// It seems like this should work:
let y = map[0] + 1; // expected reference, found integral variable
// Other things I've tried:
let value_at_0 = map[0]; // expected reference, found integral variable
let ref value_at_0 = map[0]; // expected reference, found integral variable
let y = value_at_0 + 1;
let y = (&map[0]) + 1; // expected reference, found integral variable
let y = (&mut map[0]) + 1; // binary operation `+` cannot be applied to type `&mut u64`
let y = (*map[0]) + 1; // type `u64` cannot be dereferenced
println!("{}", y);
}
The error is particularly confusing, since I would think an integral variable would be precisely the kind of thing you could add 1 to.
To show what I would like this code to do, here is how this would be implemented in Python:
>>> map = {}
>>> map.setdefault(0, 0)
0
>>> y = map[0] + 1
>>> print(y)
1
For SEO purposes, since my Googling failed, the originating error in somewhat more complex code is expected reference, found u64
For reference, the full compilation error is:
error[E0308]: mismatched types
--> ./map_weirdness.rs:8:15
|
8 | let y = map[0] + 1; // expected reference, found integral variable
| ^^^^^^ expected reference, found integral variable
|
= note: expected type `&u64`
= note: found type `{integer}`
The bug was in what was being passed to [], though the error highlighted the whole map[0], suggesting that the error was in the type of the value of map[0] when it was actually in computing the value. The correct implementation needs to pass a reference to [] as follows:
use std::collections::BTreeMap;
fn main() {
let mut map: BTreeMap<u64, u64> = BTreeMap::new();
map.entry(0).or_insert(0);
let y = map[&0] + 1;
println!("{}", y);
}

Indexing a Vec of Box Error

I was playing around with the Vec struct and ran into some interesting errors and behavior that I can't quite understand. Consider the following code.
fn main() {
let v = vec![box 1i];
let f = v[0];
}
When evaluated in the rust playpen, the code produces the following errors:
<anon>:3:13: 3:17 error: cannot move out of dereference (dereference is implicit, due to indexing)
<anon>:3 let f = v[0];
^~~~
<anon>:3:9: 3:10 note: attempting to move value to here (to prevent the move, use `ref f` or `ref mut f` to capture value by reference)
<anon>:3 let f = v[0];
^
error: aborting due to previous error
playpen: application terminated with error code 101
Program ended.
My understanding of Vec's index method is that it returns references to the values in a Vec, so I don't understand what moves or implicit dereferences are happening.
Also, when I change the f variable to an underscore, as below, no errors are produced!
fn main() {
let v = vec![box 1i];
let _ = v[0];
}
I was hoping someone could explain the errors I was getting and why they go away when switching f to _.
No idea which syntax sugar v[0] implements, but it is trying to move the value instead of getting a reference.
But if you call .index(), it works and gives you a reference with the same lifetime of the vector:
fn main() {
let v = vec![box 1i];
let f = v.index(&0);
println!("{}", f);
}
The second example works because as the value is being discarded, it doesn't try to move it.
EDIT:
The desugar for v[0] is *v.index(&0) (from: https://github.com/rust-lang/rust/blob/fb72c4767fa423649feeb197b50385c1fa0a6fd5/src/librustc/middle/trans/expr.rs#L467 ).
fn main() {
let a = vec!(1i);
let b = a[0] == *a.index(&0);
println!("{}" , b);
}
true
In your code, let f = v[0]; assigns f by value (as said in the error message, it is implicitly dereferencing) : the compiler tries to copy or move v[0] into f. v[0] being a box, it cannot be copied, thus it should be moved like in this situation :
let a = box 1i;
let b = a;
// a cannot be used anymore, it has been moved
// following line would not compile
println!("{}", a);
But values cannot be moved out of the vector via indexing, as it is a reference that is returned.
Concerning _, this code :
fn main() {
let v = vec![box 1i];
let _ = v[0];
println!("{}", _);
}
produces this error :
<anon>:4:20: 4:21 error: unexpected token: `_`
<anon>:4 println!("{}", _);
^
_ is not a variable name but a special name of rust, telling you don't care about the value, so the compiler doesn't try to copy or move anything.
You can get your original function to work by de-referencing your v[0]:
fn main() {
let v = vec![box 1i];
let f = &v[0]; // notice the &
println!("{}",f);
}
I don't know why the underscore silences your error. It should probably raise an error since the underscore alone is an invalid variable name (I think). Attempting to print it yields an error:
fn main() {
let v = vec![box 1i];
let _ = &v[0];
println!("{}",_);
}
Output:
<anon>:4:19: 4:20 error: unexpected token: `_`
<anon>:4 println!("{}",_);
The underscore is used to silence unused variable warnings (for example the compiler will yell at you if you define some_var and never use it, but won't if you define _some_var and never use it). It is also used as a fallback in a match statement to match anything that did not match other paths:
fn main() {
let v = vec![box 1i];
let f = &v[0];
match **f {
3i => println!("{}",f),
_ => println!("nothing here")
};
}
Someone smarter than me should comment on if the underscore is a valid variable name. Honestly I think the compiler shouldn't allow it.

Resources