This question already has answers here:
What are Rust's exact auto-dereferencing rules?
(4 answers)
Confusion over auto-dereferencing rules when using function
(1 answer)
Closed 4 years ago.
The following program works fine:
pub fn foo(_v: &str) -> bool {
false
}
fn main() {
let f = "hello world";
println!("{}", foo(&&&&f)); // note the number of & here
}
In fact it works on passing any number of &. How should I interpret what is going on ?
My rust version:
$ rustc --version
rustc 1.32.0-nightly (13dab66a6 2018-11-05)
From the Rust book:
Deref coercion is a convenience that Rust performs on arguments to
functions and methods. Deref coercion converts a reference to a type
that implements Deref into a reference to a type that Deref can
convert the original type into. Deref coercion happens automatically
when we pass a reference to a particular type’s value as an argument
to a function or method that doesn’t match the parameter type in the
function or method definition. A sequence of calls to the deref method
converts the type we provided into the type the parameter needs.
So basically, in function arguments the compiler will automatically remove any & written or implied until it gets to a type that can be passed to the function.
Because the compiler automatically dereferences the chain of references, you can imagine that it inserts as many * as necessary to get the right type:
foo(&&&f)
is converted to:
foo(&****(&&&f))
that leads to the right invocation:
foo(f)
The insertions of as many * as needed is actually performed by this blanket implementation of Deref trait:
impl<'a, T: ?Sized> Deref for &'a T {
type Target = T;
fn deref(&self) -> &T { *self }
}
Note: I've update my answer because in the original I used the term autoderef in the wrong way, see this post for details.
Related
This question already has answers here:
What are Rust's exact auto-dereferencing rules?
(4 answers)
Confusion over auto-dereferencing rules when using function
(1 answer)
Closed 4 years ago.
The following program works fine:
pub fn foo(_v: &str) -> bool {
false
}
fn main() {
let f = "hello world";
println!("{}", foo(&&&&f)); // note the number of & here
}
In fact it works on passing any number of &. How should I interpret what is going on ?
My rust version:
$ rustc --version
rustc 1.32.0-nightly (13dab66a6 2018-11-05)
From the Rust book:
Deref coercion is a convenience that Rust performs on arguments to
functions and methods. Deref coercion converts a reference to a type
that implements Deref into a reference to a type that Deref can
convert the original type into. Deref coercion happens automatically
when we pass a reference to a particular type’s value as an argument
to a function or method that doesn’t match the parameter type in the
function or method definition. A sequence of calls to the deref method
converts the type we provided into the type the parameter needs.
So basically, in function arguments the compiler will automatically remove any & written or implied until it gets to a type that can be passed to the function.
Because the compiler automatically dereferences the chain of references, you can imagine that it inserts as many * as necessary to get the right type:
foo(&&&f)
is converted to:
foo(&****(&&&f))
that leads to the right invocation:
foo(f)
The insertions of as many * as needed is actually performed by this blanket implementation of Deref trait:
impl<'a, T: ?Sized> Deref for &'a T {
type Target = T;
fn deref(&self) -> &T { *self }
}
Note: I've update my answer because in the original I used the term autoderef in the wrong way, see this post for details.
Could anyone tell me what's the difference between the 2 ways for returning the &str value for name of User? Both compile but I'm not sure if there's a preferred one.
pub struct User {
name: String,
}
impl User {
pub fn name(&self) -> &str {
// way1
&self.name
// way2
// self.name.as_str()
}
}
Using & on a String gives a &String and relies on deref coercion to arrive at the desired &str type, but as_str unambiguously gives a &str (while using deref coercion internally). Without deref coercion one would have to write &s[..] to turn a String s into a &str.
Deref coercion converts a reference to a type that implements the Deref trait into a reference to another type. For example, deref
coercion can convert &String to &str because String implements
the Deref trait such that it returns &str. Deref coercion is a
convenience Rust performs on arguments to functions and methods, and
works only on types that implement the Deref trait. It happens
automatically when we pass a reference to a particular type’s value as
an argument to a function or method that doesn’t match the parameter
type in the function or method definition. A sequence of calls to the
deref method converts the type we provided into the type the
parameter needs. -- The Rust Programming Language (Chapter 15)
Some programmers may prefer one for its brevity, while others may prefer the other one for its clarity.
This question already has answers here:
How to write a trait bound for adding two references of a generic type?
(1 answer)
How do I require a generic type implement an operation like Add, Sub, Mul, or Div in a generic function?
(2 answers)
Closed 4 years ago.
I'm trying to use the sprs crate (version 0.6.3) to manipulate sparse vectors. I'd like to add two vectors together.
I started off with an implementation of the Add trait, then simplified this to an implementation function. Finally, I've boiled down the problem to a simple generic function.
// This works: the scalar type `u64` is fixed here
fn adder(first: &CsVec<u64>, second: &CsVec<u64>) -> CsVec<u64> {
first + second
}
// When I try to make the scalar type generic, it doesn't work
fn adder2<T>(first: &CsVec<T>, second: &CsVec<T>) -> CsVec<T>
where
CsVec<T>: Add,
T: Add + Debug,
{
first + second
}
The first version compiles fine, but I'd like to know why the second version won't compile. I get this error message:
error[E0369]: binary operation `+` cannot be applied to type `&sprs::sparse::CsVecBase<std::vec::Vec<usize>, std::vec::Vec<T>>`
--> libp3prime/src/lib/datacache.rs:62:5
|
62 | first + second
| ^^^^^^^^^^^^^^
|
= note: an implementation of `std::ops::Add` might be missing for `&sprs::sparse::CsVecBase<std::vec::Vec<usize>, std::vec::Vec<T>>`
I don't really understand the error message. I know that you can add two CsVecs together, since adder() compiles, so I am a bit lost.
The two vectors should add together.
Be sure that the trait bounds defined on the function match the behavior used in the function.
first and second are not CsVec<T>, but &CsVec<T>. In Rust, &X is a different type from X. You need a trait bound that says that you can add two &CsVec<T>s and get a CsVec<T> as output:
fn adder2<'a, T>(first: &'a CsVec<T>, second: &'a CsVec<T>) -> CsVec<T>
where
&'a CsVec<T>: Add<Output = CsVec<T>>,
{
first + second
}
No bounds on T are needed in this example.
The 'a lifetime parameter in this case was passed in to the function. Sometimes it is useful to define a trait bound on a reference inside the function, for example, to use + on references to local variables. In that case you would want to use the higher-ranked trait bound for<'a> &'a CsVec<T>: Add<Output = CsVec<T>> instead. See the linked questions below for more information.
Lukas Kalbertodt points out that it may sometimes be more flexible to say "I just want to add two &CsVec<T>s, and I will return whatever type that operation gives me", which you can do by returning <&'a CsVec<T> as Add>::Output:
fn adder2<'a, T>(first: &'a CsVec<T>, second: &'a CsVec<T>) -> <&'a CsVec<T> as Add>::Output
where
&'a CsVec<T>: Add,
{
first + second
}
In this case the output type does not have to be exactly CsVec<T>, but when it is, it works the same way as the first version.
Related
How to write a trait bound for adding two references of a generic type?
How do I require a generic type implement an operation like Add, Sub, Mul, or Div in a generic function?
This question already has answers here:
What is the return type of the indexing operation?
(2 answers)
Closed 4 years ago.
I check Index trait in doc and find return type of index() is &T.
Then I write this function to get value from vector:
fn get_value_test(a: usize, v: &Vec<i32>) -> i32 {
v[a]
}
My question is: why v[a] is i32 but &i32? Because i32 ...have a known size at compile time are stored entirely on the stack, so copies of the actual values are quick to make? (from here)
It looks like Rust have hidden rule to convert type in this situation?
There was a small misguidance here. Although the method prototype for Index<Idx> is fn index(&self, index: Idx) -> &T, the syntactical operator x[i] does dereference the output from that &T:
container[index] is actually syntactic sugar for *container.index(index) [...]. This allows nice things such as let value = v[index] if the type of value implements Copy.
So there you go. Your function is indeed returning a copy of the value from the vector, but not from an implicit conversion. If the original intent was to really retrieve a reference to that value, you would do &x[i].
See also:
Meaning of the ampersand '&' and star '*' symbols in Rust
Does Rust automatically dereference primitive type references?
This question already has answers here:
What's an idiomatic way to print an iterator separated by spaces in Rust?
(4 answers)
Closed 5 years ago.
Currently the SliceConcatExt seems to be very specifically crafted for slices or vectors of Strings, even though it arbitrarily constrains its use. That particular use-case is reflected in the trait name as well, after all, it is called SliceConcatExt for a reason.
Is there a more general connect() implementation which would take any Iterator over items supporting the Str trait ?.
If not, are there any plans to remedy this ?
Example
use std::iter::IntoIterator;
fn connected<S, I>(s: I) -> String
where S: Str,
I: IntoIterator<Item=S> {
// have
s.into_iter().collect::<Vec<S>>().connect(", ")
// want
// s.into_iter().connect(", ")
// error: type `<I as core::iter::IntoIterator>::IntoIter` does not implement any method in scope named `connect`
// tests/lang.rs:790 s.into_iter().connect(", ")
}
connected(&["foo", "bar"]);
One could possibly implement SliceConcatExt for any iterator with item type Str, but I have the suspicion that connect() currently is just unnecessarily specialized, which might be fixable until Rust beta.
Using rustc 1.0.0-nightly (522d09dfe 2015-02-19) (built 2015-02-19)
The closest solution I know of would be to use Itertools::intersperse:
#![feature(core)]
extern crate itertools;
use std::iter::IntoIterator;
use itertools::Itertools;
fn connected<'a, S, I>(s: I) -> String //'
where S: Str,
I: IntoIterator<Item=&'a S> //'
{
s.into_iter().map(|s| s.as_slice()).intersperse(", ").collect()
}
fn main() {
println!("{}", connected(&["foo", "bar"]));
}