What's the practical use of Option in rust? [closed] - rust

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
Consider this example
fn main() {
let mut i: Option<i32> = None;
//after some processing it got some value of 55
i = Some(55);
println!("value is {:?}", i.unwrap());
}
In go, nil represents the zero-value of that type.
However in rust, it represents absence of a value. How is absence of a value useful in practice?
When a variable with a type is declared, it must have some value either initialized or un-initialized. Why will one declare it to have it absent?
Also please explain, at what point the memory is allocated for i during the initial declaration or when i gets some value?
I might be asking a stupid question, but want to get my head around the need of this concept.

How is absence of a value useful in practice?
A simple example is a function that looks for the first matching element in a collection. It may find it, and return it, or not find any.
The docs give a few more cases:
Initial values
Return values for functions that are not defined over their entire input range (partial functions)
Return value for otherwise reporting simple errors, where None is returned on error
Optional struct fields
Struct fields that can be loaned or "taken"
Optional function arguments
Nullable pointers
Swapping things out of difficult situations
Now, you may ask: why don't we use one of the values to mark an empty one? For two reasons:
There are cases where you do not have a valid "zero-value" or a valid "invalid" value. In this case, you have to use some flag somewhere else to store the fact that something is invalid.
In general, it is simpler to use the same solution everywhere than having to mark and document which is the "none" value.
Why will one declare it to have it absent?
This is different than initialized/uninitialized values. Option is simply a type that contains either "nothing" (None) or a "value" of some type (Some(value))
You can conceptually see it as a struct with a flag and some space for the value itself.
Also please explain, at what point the memory is allocated for i during the initial declaration or when i gets some value?
That depends on the implementation. One could decide to implement Option using a pointer to the value, which means it could delay allocating.
However, the most likely implementation is avoiding pointers and keeping the value plus an extra flag. Note that, for some types, you can also optimize further and avoid the flag altogether. For instance, if you have an Option of a pointer, you can simply use the zero value for None. In fact, Rust does such a thing for types like Option<Box<T>>.

Related

Rust ownership, how to return a ref for a struct? [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed last month.
Improve this question
I generate an instance A from instance B's method and managed by B, but I want return A. How to implement this?
For this codes (playground link) I want generate a history for the RedPacket, managed by RedPacket (push into the Vec) and return history ref to print, but the compiler throw out: cannot move out of history because it is borrowed
How implement this? Or is this a right way in rust world?
It isn’t right way in only “Rust world”, it’s completely wrong in whole programming world. 1. Local variables are stored on the stack, so after function call they are deleted. 2. You’re trying to move your local variable after borrowing it. You are trying to return reference to invalid buffer, and compiler prevents it.
You can return a reference to the last Vec member though:
self.histories.push(history);
self.histories.last()

Is it idiomatic rust to accept arguments that `impl Borrow<T>` to abstract over references and values of T? [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 1 year ago.
Improve this question
I find myself writing functions that accepts arguments as Borrow<T> so that it accepts both values and references transparently.
Example:
use std::borrow::Borrow;
#[derive(Debug, Copy)]
struct Point {
pub x: i32,
pub y: i32,
}
pub fn manhattan<T, U>(p1: T, p2: U) -> i32
where
T: Borrow<Point>,
U: Borrow<Point>,
{
let p1 = p1.borrow();
let p2 = p2.borrow();
(p1.x - p2.x + p1.y - p2.y).abs()
}
That can be useful to implement std:ops like Add, which would otherwise require a lot of repetition to support references transparently.
Is this idiomatic? Are there drawbacks?
I think there are two parts to this question.
1. Is the Borrow trait the idiomatic way to abstract over ownership in Rust?
Yes. If what you intend is to write a function that either takes a Foo or a &Foo, F: Borrow<Foo> is the right bound to use. AsRef, on the other hand, is usually only implemented for things that are reference-like, and not for owned values.
2. Is it idiomatic in Rust to abstract over ownership at all?
Sometimes. This is an interesting question because there is a subtle but important distinction between a function like manhattan and how Borrow is idiomatically used.
In Rust, whether a function needs to own its arguments or merely borrow them is an important part of the function's interface. Rustaceans, as a rule, don't mind writing & in a function call because it's a syntactic marker of a relevant semantic fact about the function being called. A function that can accept either Point or &Point is no more generally useful than the one that can accept only &Point: if you have a Point, all you have to do is borrow it. So it's idiomatic to use the simpler signature that most accurately documents the type the function really needs: &Point.
But wait! There are other differences between those ways of accepting arguments. One difference is call overhead: a &Point will generally be passed in a single pointer-sized register, while a Point may be passed in multiple registers or on the stack, depending on the ABI. Another difference is code size: each unique instantiation of <T: Borrow<Point>> represents a monomorphization of the function, which bloats the binary. A third difference is drop order: if Point has destructors, a function that accepts T: Borrow<Point> will call Point::drop internally, while a function that accepts &Point will leave the object in place for the caller to deal with. Whether this is good or bad depends on the context; for performance, though, it's usually irrelevant (if you assume the Point will eventually be dropped anyway).
A function accepting T: Borrow<Point> suggests that it's doing something with T internally for which a mere &Point might be suboptimal. Drop order is probably the best reason for doing this (I wrote more about this in this answer, although the puts function I used as an example isn't a particularly strong one).
In the case of manhattan drop order is irrelevant, because Point is Copy (trivially copied types may not have drop glue). So there is no performance advantage from accepting Point as well as &Point (and although a single function isn't likely to make much difference one way or another, if generics are used pervasively, the cost to code size may well be a disadvantage).
There is one more reason to avoid using generics unnecessarily: they interfere with type inference and can decrease the quality of error messages and suggestions from the compiler. For instance, imagine if Point only implemented Clone (not Copy) and you wrote manhattan(p, q) and then used p again later in the same function. The compiler would warn you that p was used after being moved into the function and suggest adding a .clone(). In fact, the better solution is to borrow p, and if manhattan takes references the compiler will enforce that you do just that.
The fact Point is small (so overhead to using it as a function argument is probably minimal) and Copy (so has no drop glue to worry about) raises another question: should manhattan simply accept Point and not use references at all? This is an opinion-based question and really it comes down to which better fits your mental model. Either accept &Point, and use & when a caller has an owned value, or accept Point, and use * when a caller has a reference - there is no hard and fast rule.
What is an appropriate use of Borrow, then?
The argument above strongly depends on the fact that references are easy to take anywhere, so you may as well take them concretely in the caller as abstractly inside the generic function. One time this is not the case is when the borrowed-or-owned type is not passed directly to the function, but wrapped in another generic data structure. Consider sorting a slice of Point-like things by their distance from (0, 0):
fn sort_by_radius<T: Borrow<Point>>(points: &mut [T]) {
points.sort_by_key(|p| {
let Point { x, y } = p.borrow();
x * x + y * y
});
}
In this case it's definitely not the case that the caller with a &mut [Point] can simply borrow it to get a &mut [&Point]. Yet we would like sort_by_radius to be able to accept both kinds of slices (without writing two functions) so Borrow<Point> comes to the rescue. The difference between sort_by_radius and your version of manhattan is that T is not being passed directly to the function to be immediately borrowed, but is a part of the type that sort_by_radius needs to treat like a Point in order to perform a task ultimately unrelated to borrowing (sorting a slice).

Why Rust allows declaring same variable name twice in a scope? [duplicate]

This question already has an answer here:
What is the rationale behind allowing variable shadowing in Rust? [closed]
(1 answer)
Closed 2 years ago.
First time I am encountering a typed language allowing to declare a variable name twice in the same scope. Wouldn't there be a chance to override an existing variable by mistake? What advantage does it bring?
There is a chapter in the book about this.
Shadowing is different from marking a variable as mut, because we’ll get a compile-time error if we accidentally try to reassign to this variable without using the let keyword. By using let, we can perform a few transformations on a value but have the variable be immutable after those transformations have been completed.
The other difference between mut and shadowing is that because we’re effectively creating a new variable when we use the let keyword again, we can change the type of the value but reuse the same name. For example, say our program asks a user to show how many spaces they want between some text by inputting space characters, but we really want to store that input as a number
let spaces = " "; // String
let spaces = spaces.len(); // number
In short, it allows you to "modify" a value, in a way that is technically immutable. Rust ensures that you cannot use the shadowed variable, so it's perfectly typesafe.
I'm no Rust expert, but from a language design perspective it's an interesting thing to encourage. But I think the point is to discourage the use of mutable values whenever possible by allowing you to immutably override a name with a new type and value.

Why can't Rust do more complex type inference? [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 4 years ago.
Improve this question
In Chapter 3 of The Rust Programming Language, the following code is used as an example for a kind of type inference that Rust cannot manage:
fn main() {
let condition = true;
let number = if condition { 5 } else { "six" };
println!("The value of number is: {}", number);
}
with the explanation that:
Rust needs to know at compile time what type the number variable is, definitively, so it can verify at compile time that its type is valid everywhere we use number. Rust wouldn’t be able to do that if the type of number was only determined at runtime; the compiler would be more complex and would make fewer guarantees about the code if it had to keep track of multiple hypothetical types for any variable.
I'm not certain I understand the rationale, because the example does seem like something where a simple compiler could infer the type.
What exactly makes this kind of type inference so difficult? In this case, the value of condition can be clearly inferred at compile time (it's true), and so thus the type of number can be too (it's i32?).
I can see how things could become a lot more complicated, if you were trying to infer types across multiple compilation units for instance, but is there something about this specific example that would add a lot of complexity to the compiler?
There are three main reasons I can think of:
1. Action at a distance effects
Let's suppose the language worked that way. Since we're extending type inference, we might as well make the language even smarter and have it infer return types as well. This allows me to write something like:
pub fn get_flux_capacitor() {
let is_prod = true;
if is_prod { FluxCapacitor::new() } else { MovieProp::new() }
}
And elsewhere in my project, I can get a FluxCapacitor by calling that function. However, one day, I change is_prod to false. Now, instead of getting an error that my function is returning the wrong type, I will get errors at every callsite. A small change inside one function has lead to errors in entirely unchanged files! That's pretty weird.
(If we don't want to add inferered return types, just imagine it's a very long function instead.)
2. Compiler internals exposed
What happens in the case where it's not so simple? Surely this should be the same as the above example:
pub fn get_flux_capacitor() {
let is_prod = (1 + 1) == 2;
...
}
But how far does that extend? The compiler's constant propagation is mostly an implementation detail. You don't want the types in your program to depend on how smart this version of the compiler is.
3. What did you actually mean?
As a human looking at this code, it looks like something is missing. Why are you branching on true at all? Why not just write FluxCapacitor::new()? Perhaps there's logic missing to check and see if a env=DEV environment variable is missing. Perhaps a trait object should actually be used so that you can take advantage of runtime polymorphism.
In this kind of situation where you're asking the computer to do something that doesn't seem quite right, Rust often chooses to throw its hands up and ask you to fix the code.
You're right, in this very specific case (where condition=true statically), the compiler could be made able to detect that the else branch is unreachable and therefore number must be 5.
This is just a contrived example, though... in the more general case, the value of condition would only be dynamically known at runtime.
It's in that case, as other have said, that inference becomes hard to implement.
On that topic, there are two things I haven't seen mentioned yet.
The Rust language design tends to err on the side of doing things as
explicitly as possible
Rust type inference is only local
On point #1, the explicit way for Rust to deal with the "this type can be one of multiple types" use case are enums.
You can define something like this:
#[derive(Debug)]
enum Whatsit {
Num(i32),
Text(&'static str),
}
and then do let number = if condition { Num(5) } else { Text("six") };
On point #2, let's see how the enum (while wordier) is the preferred approach in the language. In the example from the book we just try printing the value of number.
In a more real-case scenario we would at one point use number for something other than printing.
This means passing it to another function or including it in another type. Or (to even enable use of println!) implementing the Debug or Display traits on it. Local inference means that (if you can't name the type of number in Rust), you would not be able to do any of these things.
Suppose you want to create a function that does something with a number;
with the enum you would write:
fn do_something(number: Whatsit)
but without it...
fn do_something(number: /* what type is this? */)
In a nutshell, you're right that in principle it IS doable for the compiler to synthesize a type for number. For instance, the compiler might create an anonymous enum like Whatsit above when compiling that code.
But you - the programmer - would not know the name of that type, would not be able to refer to it, wouldn't even know what you can do with it (can I multiply two "numbers"?) and this would greatly limit its usefulness.
A similar approach was followed for instance to add closures to the language. The compiler would know what specific type a closure has, but you, the programmer, would not. If you're interested I can try finding out discussions on the difficulties that the approach introduced in the design of the language.

F# when should you use tuple and when should you use struct? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
What are the underlying differences between F# tuples and structs. They can both hold multiple variables, but I just want to know when one is used over the other.
To be more specific, I am trying to pass a bunch of parameters through a bunch of functions with a new variable being accumulated each time. For example, I start with param1 and pass it into func1 which returns (param1,param2). This tuple (or struct) then gets passed to func2 which returns (param1,param2,param3), and so on.
My current thoughts are this: with a tuple, I can always hold just the right amount of arguments, but I give up a uniform format for the data, and in the end, I would have to pack and repack a tuple of about 10 elements. With a struct, I have the advantage of uniformity of the parameters, but the problems is, I would have to null specify the parameters in the beginning.
In F#, tuples are represented using Tuple<T1, T2> which is a reference type. On the other hand, structures are value types and so they are allocated on stack rather than on the heap (which may sometimes be faster). So my general rules are:
Tuples have nice syntactic support in F#, so use tuples by default because they make your code nicer. In most cases, the performance is similar and you do not need to worry about it (it depends on the use - tuples are not always slower).
When your tuples get more complicated (say, more than 3 elements), it makes sense to use a type with named members (like a record or an object type).
When allocating a large array of tuples or structs, it is better to use structs. You can either define your own struct or use standard .NET structs like KeyValuePair<K, V>.
To give an anecdotal evidence, in Deedle we are using structs for the internals (time-series is stored as an array of structs), but not for the public API, which uses tuples.

Resources