How to transform a container of string slices in Rust? - rust

Suppose I have a Vec<&str> (I have no control over this type) and I need to transform each string. Since the elements are string slices (which don't own the string), I need to store the actual strings somewhere.
I thought about storing the new strings in a container that's able to grow without reallocating. So I came up with the following:
use std::collections::VecDeque;
fn transform(s: &str) -> String {
s.to_owned() + "blah"
}
pub fn func<'a>(strings: &mut Vec<&'a str>, string_storage: &'a mut VecDeque<String>) {
for string in strings.iter_mut() {
string_storage.push_back(transform(string));
*string = string_storage.back().unwrap().as_str();
}
}
Of course, it doesn't work (I get error[E0502]: cannot borrow `*string_storage` as mutable because it is also borrowed as immutable). I understand why the error happens, but I've struggled for some time to figure out a solution.
The best I could come up with is to separate the pushes and assignments into two loops:
pub fn func<'a>(strings: &mut Vec<&'a str>, string_storage: &'a mut Vec<String>) {
for string in strings.iter() {
string_storage.push(transform(string));
}
for (i, string) in strings.iter_mut().enumerate() {
*string = string_storage[i].as_str();
}
}
But it seems weird to have to iterate twice. Is there a simpler solution?

Related

Returning an array/slice of `u32` from a function

I have a reasonably simple function (let's call it intersection) that takes two parameters of type &[u32] and I'd like the return type to be &[u32]. This function takes in two slices(arrays?), and returns a new slice(array?) containing elements that are in both slices.
pub fn intersection<'a>(left: &'a [u32], right: &'a [u32]) -> &'a [u32] {
let left_set: HashSet<u32> = left.iter().cloned().collect();
let right_set: HashSet<u32> = right.iter().cloned().collect();
// I can't figure out how to get a
// `&[u32]` output idiomatically
let result: &[u32] = left_set
.intersection(&right_set)
.into_iter()
.....
.....
result //<- this is a slice
}
I suppose I could do something like create a Vec<u32> but then borrow checker doesn't like me returning that Vec<u32>.
pub fn intersection<'a>(left: &'a [u32], right: &'a [u32]) -> &'a [u32] {
.....
.....
let mut result: Vec<u32> = left_set
.intersection(&right_set)
.into_iter()
.cloned()
.collect();
result.sort();
result.as_slice() //<-- ERROR cannot return reference to local variable
// `result` returns a reference to data owned by the current function
}
I'm probably missing a trick here. Any advice on how to do this idiomatically in Rust?
This function takes in two arrays
No, it takes two slices.
I'm probably missing a trick here. Any advice on how to do this idiomatically in Rust?
There is no trick and you can't. A slice is a form of borrow, by definition a slice refers to memory owned by some other collection (static memory, a vector, an array, ...).
This means like every other borrow it can't be returned if it borrows data from the local scope, that would result in a dangling pointer (as the actual owner will get destroyed when the scope ends).
The correct thing to do is to just return a Vec:
pub fn intersection<'a>(left: &'a [u32], right: &'a [u32]) -> Vec<u32> {
left.iter().collect::<HashSet<_>>().intersection(
&right.iter().collect()
).map(|&&v| v).collect()
}
Or if it's very common for one of the slices to be a subset of the other and you're happy paying for the check (possibly because you can use something like a bitmap) you could return a Cow and in the subset case return the subset slice:
pub fn intersection<'a>(left: &'a [u32], right: &'a [u32]) -> Cow<'a, [u32]> {
if issubset(left, right) {
Cow::Borrowed(left)
} else if issubset(right, left) {
Cow::Borrowed(right)
} else {
Cow::Owned(
left.iter().collect::<HashSet<_>>().intersection(
&right.iter().collect()
).map(|&&v| v).collect()
)
}
}

Assembling a string and returning it with lifetime parameters for a l-system

I'm trying to implement a L-System struct and am struggling with it. I already tried different approaches but my main struggle comes from lifetime of references. What I'm trying to achieve is passing the value of the applied axioms back to my system variable, which i passed with the necessary lifetime in apply_axioms_once.
use std::collections::HashMap;
struct LSytem<'a> {
axioms: HashMap<&'a char, &'a str>,
}
impl<'a> LSytem<'a> {
fn apply_axioms_once(&mut self, system: &'a mut str) -> &'a str {
let mut applied: String = String::new();
for c in system.chars() {
let axiom = self.axioms.get(&c).unwrap();
for s in axiom.chars() {
applied.push(s);
}
}
system = applied.as_str();
system
}
fn apply_axioms(&mut self, system: &'a str, iterations: u8) -> &'a str {
let mut applied: &str = system;
// check for 0?
for _ in 0..iterations {
applied = self.apply_axioms_once(applied);
}
&applied
}
}
I already read a couple of similar questions, but still can't quite wrap my head around it. What seems to be the most on point answer is https://stackoverflow.com/a/42506211/18422275, but I'm still puzzled about how to apply this to my issue.
I am still a beginner in rust, and way more bloody than i thought.
This can't work because you return a reference of a data created inside the function (so the given data has a lifetime until the end of the function scope, the returned reference would point to nothing).
You shoud try to return String from your functions instead, so the returned data can be owned.
I made this example to try out:
use std::collections::HashMap;
struct LSytem<'a> {
axioms: HashMap<&'a char, &'a str>,
}
impl<'a> LSytem<'a> {
fn apply_axioms_once(&mut self, system: &String) -> String {
let mut applied: String = String::new();
for c in system.chars() {
let axiom = self.axioms.get(&c).unwrap();
for s in axiom.chars() {
applied.push(s);
}
}
applied
}
fn apply_axioms(&mut self, system: &String, iterations: u8) ->String{
let mut applied = String::from(system);
// check for 0?
for _ in 0..iterations {
applied = self.apply_axioms_once(system);
}
applied
}
}
fn main() {
let mut ls = LSytem {axioms: HashMap::new()};
ls.axioms.insert(&'a', "abc");
let s = String::from("a");
ls.apply_axioms(&s,1);
}

Get `&'a str` with longer lifetime than the current function from a `String` [duplicate]

This question already has answers here:
Return local String as a slice (&str)
(7 answers)
Closed 3 years ago.
Inside a function that returns a &'a str, I created a String.
I want to return a &'a str with the same contents than this String
fn givesString(A: &str) -> String {
String::from("example")
}
pub struct S<'a> {
pub strField: &'a str,
}
impl<'a> S<'a> {
pub fn from_String_A(A: &'a str) -> Result<Self, Box<dyn std::error::Error>> {
let processed_String = givesString(&A);
let processed_str: &'a str = a_processed_String.as_str();
Ok(Self {
strField: processed_str,
})
}
}
playground
which doesn't compile: the borrowed value a_processed_String does not live long enough, since it's dropped at the end of the function.
I understand the String will be dropped no matter what.
But now, if I create a &'a str and pass it into the return value, it compiles with no issue:
fn givesString(A: &str) -> String {
String::from("example")
}
pub struct S<'a> {
pub strField: &'a str,
}
impl<'a> S<'a> {
pub fn from_String_A(A: &'a str) -> Result<Self, Box<dyn std::error::Error>> {
let longlife: &'a str = "hello from the outside of this fn";
Ok(Self {
strField: longlife,
})
}
}
playground
So, is it possible to create a new &'a str that, instead of borrowing from the String that will be dropped, simply points to some memory with a lifetime 'a that holds the same contents as the String ?
There has been countless similar questions where the answer was to simply return a String instead, but let's assume I cannot touch the code for givesString or the struct S
This depends on how hard you need to try...
In the second example, longlife is a literal &'static str. Since 'static lives for at least as long as any 'a no matter what 'a is, it's valid to be assigned to the return value via S. This is not very useful in the general case, though, since you can't modify a &'static str for obvious reasons.
I'd highly recommend to re-structure the code so there is an owner, S seems to be a candidate.
You can make this work via std::mem::forget: You create the String, forget about it and manually derive a forced &'static str from it. This quite simly means you leak the memory of the allocated String, making it live for at least as long as any 'a. Most people would consider this simply a bug or at least a really ugly, ugly hack. But it's possible.

How to store struct reference in a vec and use the struct elsewhere later on?

I want to create a struct, store a reference to it in a vec, and then use the struct later on:
pub struct NonTerminal {
name: String
}
impl<'a> NonTerminal {
pub fn new(grammar: &'a mut Grammar, name: &str) -> &'a NonTerminal {
let t = NonTerminal {
name: name.to_string()
};
grammar.non_terminals.push(t);
grammar.non_terminals.last().unwrap()
}
}
pub struct Grammar<'a> {
non_terminals: Vec<NonTerminal>
}
pub fn create_rules<'a>(g: &'a mut grammar::Grammar<'a>) {
let sum = grammar::NonTerminal::new(g, "Sum");
let product = grammar::NonTerminal::new(g, "Product");
// this fails: "cannot borrow `*g` as mutable more than once at a time, second mutable borrow occurs here"
// ...
// use the sum and product vars when constructing rules
}
How to approach this?
You can't do this. This is simply the classical "vector push" example in disguise. Let's take a look at this simplified, but semantically equivalent code:
let mut v = Vec::new();
v.push(27);
let twenty_seven = v.last().unwrap();
v.push(3);
println!("{}", twenty_seven);
This does not compile and will never compile in Rust as this is inherently memory unsafe. You have a reference (a pointer) to an element in the vector. But the call to Vec::push might reallocate, invalidating all references to its elements. In C++ it would compile, but lead to UB because you would attempt to read uninitialized or unallocated memory.
The "answer" to your problem is... not simple. You have to think of another structure for your program. Sometimes, using reference counted smart pointers (like Rc) is useful. And it would easily solve your problem. But in many other situations you are better of completely rethinking your application.
Exactly one &mut borrow of data may occur at any given time.
I'd suggest inverting things so that Grammar has a function to add NonTerminals
pub struct NonTerminal {
name: String
}
impl NonTerminal {
pub fn new(name: &str) -> NonTerminal {
Self {
name: name.to_string()
}
}
}
pub struct Grammar {
pub non_terminals: Vec<NonTerminal>
}
impl Grammar {
fn add_non_terminal(&mut self, s: &str) -> &NonTerminal {
self.non_terminals.push(NonTerminal::new(s));
self.non_terminals.last().unwrap()
}
}
pub fn main() {
let mut g = Grammar {
non_terminals: vec![]
};
let product_ref = g.add_non_terminal("Product");
let sum_ref = g.add_non_terminal("Sum");
}
Updated based on feedback from #Lukas Kalbertodt.
Can iterate over all non-terminals via g.non_terminals

Rust function that takes an iterator, and returns an iterator with mutated items?

I want to write a function that takes one iterator as input, and return an iterator with the same items but mutated. Like in this example:
fn greet(name: &mut String) {
name.insert_str(0, "Hello ");
}
fn to_greetings(names: impl Iterator<Item = String>) -> impl Iterator<Item = String> {
names.inspect(|name| greet(name))
}
This does not compile, since name isn't mutable. I have tried adding mut in various places without really understanding what I am doing, but without any success. How do I fix the above code?
I guess I need to make the items mutable, but apparently Item = mut String is a syntax error.
If the item type of an iterator is String, the iterator yields owned strings. The iterator transfers ownership of these strings to the consumer. The consumer can modify the strings, since it owns them.
However, you can't use inspect() to modify the elements of an iterator. The closure passed to inspect() receives an immutable reference to the items of the iterator, which can't be used to modify the items. The correct iterator adapter to modify items is map(), so this works (but it doesn't feel particularly idiomatic to me):
fn to_greetings<I>(names: I) -> impl Iterator<Item = String>
where
I: Iterator<Item = String>,
{
names.map(|mut name| { greet(&mut name); name })
}
If you want to actually modify the strings in some underlying container, e.g. a vector of strings, you need a different approach though. A mutable iterator over a container of strings has an item type of &mut String, so you'd need to use something like
fn to_greetings<'a, I>(names: I) -> impl Iterator<Item = &'a mut String>
where
I: Iterator<Item = &'a mut String>,
{
names.map(|name| { greet(name); name })
}

Resources