Difference between Arc and AtomicPtr

Difference between task and thread is fundamental, not pedantic. Similar to difference between OS and Software.

I would say String is not a Smart Pointer because as well as carrying that pointer, length and capacity in some structure it also allocates the memory used for the content of the string. There is a pointer in there somewhere. Smart or otherwise. But String is about the data it contains.

Yes it is.

I agree. Pretty fundamental.

Its just that in the context of discussing some async code, using tokio say, one might casually say "why not spawn a new thread to do such and such" and not expect ones colleague to start quibbling about how "you mean task don't you". I'd hope my colleague is smart enough to know what I mean when speaking casually.

How can it be so? All my life a "pointer" has been a simple memory address. Smart pointers are more than that.

I see it like saying

struct Quantity {
    u32: value,
    String: name,
}

is a u32. Well, sure it has one in it.

String, Vec, MutexGuard, etc. are all certainly smart pointers as the term is conventionally defined. Cow is the only one I can see as a bit arguable since it does not always involve indirection (however, note that e.g. Cow<str> is clearly a smart pointer since it does always involve indirection).

The term "smart pointer" comes from C++ where it basically just means any pointer-like type that overloads operator* and operator-> (and usually has a destructor, although I guess that's not strictly necessary as you could do all the "smart" things in the overloads...)

5 Likes

Same way an int isn't "just" memory address.

Coming from C, my personal use of the term "pointer" (apart from the Rust world), is what would be called "raw pointer" in the Rust world. But I can see how it may be a valid p.o.v. to consider a "smart pointer" to be a pointer as well (in that matter, the Rust reference also lists smart pointer in the "pointer type" subsection).

What do you mean exactly? Cow does implement Deref, and you can access the value pointed to with * (though not move out unless Deref::Target is Copy):

use std::borrow::Cow;

fn main() {
    let s: String = "Hello".to_string();
    let i: i32 = 15;
    let c1: Cow<String> = Cow::Borrowed(&s);
    let c2: Cow<String> = Cow::Owned(s.clone());
    let c3: Cow<i32> = Cow::Borrowed(&i);
    let c4: Cow<i32> = Cow::Owned(i);
    let ref1: &String = &*c1;
    let ref2: &String = &*c2;
    let ref3: &i32 = &*c3;
    let ref4: &i32 = &*c4;
    println!(
        "ref1 = {}, ref2 = {}, ref3 = {}, ref4 = {}",
        ref1, ref2, ref3, ref4
    );
    // These two fail:
    // let _a = *c1;
    // let _b = *c2;
    let _c = *c3;
    let _d = *c4;
}

(Playground)

I'm not sure, what exactly you meant though.


Note, you can also call methods:

use std::borrow::Cow;

fn main() {
    let s: String = "Hello".to_string();
    let c1: Cow<String> = Cow::Borrowed(&s);
    let c2: Cow<String> = Cow::Owned(s.clone());
    println!("lengths = {} and {}", c1.len(), c2.len());
}

(Playground)


Maybe you can explain what you meant with "indirection". I think "dereferencing" takes place in these examples.

Yes, Cow<'a, T> has the syntax of a pointer, but it doesn't actually point: it contains a T inline.

That is, when it's Owned. If it's the Borrowed variant, then it is a pointer (of the "reference" variety), which is of course the whole point (no pun intended).

(And when T is non-Clone, such as str, Cow<T> might not ever contain a T at all because of how Owned is defined. Cow<'a, str> contains either Borrowed(&'a str) or Owned(String), both of which are pointers to str, and the Cow itself also dereferences to str.)

Put another way, there are three elements people usually talk about when it comes to smart pointers:

  1. Does it have the syntax of a pointer? (In C++, does it implement operator*? In Rust, does it implement Deref?)
  2. Is it semantically a pointer, i.e. does it refer to something that is stored not in the object itself but at some other location? This is what I mean by "indirection".
  3. Does it have some additional behavior that "raw" pointers don't have, which makes it "smart"? Usually this means having drop glue.

The easy cases are the ones that meet all three criteria. String is a smart pointer to str: you can dereference it with * and . to get a str, the data is not stored inline but in an allocation (the "pointer" part), and when you drop a non-empty String it deallocates the backing buffer (the "smart" part).

Cow<'_, T> is a little weird because of point 2: sometimes it contains an indirection, sometimes it contains a T directly. Does that make it sometimes a smart pointer? :man_shrugging:

&T and &mut T are a little weird because of point 3: depending on how you define "behavior" you might consider references to be smarter than raw pointers, or just dumb pointers with extra compile time rules. :man_shrugging:

When people say to not implement Deref for things that aren't smart pointers they're usually talking about the semantics of pointers. Obviously, anything that is Deref is syntactically a pointer by definition. But if there's no indirection, that might confuse your intuition about how the type works.

1 Like

Who says that? There’s things that legitimately implement Deref without being a smart pointer. For example once_cell::[un]sync::Lazy, or the already mentioned Cow<'_, T> type in the Owned case (at least when T: Clone). Or things like ManuallyDrop or AssertUnwindSafe.

When considering things like Pin, it’s also apparent that asking a question like “is Foo a smart pointer” is not always a good question; better be more precise and ask “is Foo<T> a smart pointer to T”? Because you have things like “if P is a smart pointer to T, then Pin<P> is a smart pointer to T”. Or with cell-types, one might consider Rc<RefCell<T>> to be a smart pointer to T, even though RefCell doesn’t implement Deref. If you’d consider Rc<RefCell<T>> to be a smart pointer to T, then the rule could be “if P is a smart pointer to RefCell<T>, then P is (also) a smart pointer to T”.

Lots of people. Previous discussions:

Note that I'm merely attempting to interpret that commonly cited rule in light of how people use the term "smart pointer". I don't intend to start a debate about whether it's a good rule or not.

1 Like

I guess that depends on the definition of "smart pointer". I haven't found any "official" definition (e.g. in the reference) yet. But what I found is this:

That's from the official documentation. Of course, there is only a "should" and not a "must" there, and Deref is also implemented for plain references.

I would even claim that Cow::Owned can could (on a high abstraction level) be considered a smart pointer. It's not much different than Box, except that the value lives on the stack instead of the heap, right? If we disregard the underlying memory management, it's not much different from Box.

P.S.: Actually the inner type of a Cow is ?Sized. I guess then it then matters where the Cow is used whether the storage happens on the heap or stack (but in the same space where the Cow lives, which may be an argument to say there is no "indirection"). (The inner type of a Cow is ?Sized.)

P.P.S.: I also understand the intuition that String isn't a smart pointer (again without wanting to say it isn't either). I guess without any clear definition, everyone can have their own definition. It's still interesting (for me) to understand what the term usually means, and @trentj's note that the term originates from operator overloading in C++ was interesting.

I'm not sure I know what you mean. I agree, an int is not a memory address. It's an integer variable, a value, at least in C/C++. Rust has same in u32, 64 etc. ints have a type and operations that can performed on them etc. Not including dereferencing, that is for pointers, which are memory addresses.

Anyway, seems everyone has there own idea of what can be called a "pointer", "smart pointer" and the like. All of which have technical differences in different programming languages.

After following some of your links, I think the thing is: Deref shouldn't be implemented to just "wrap" a type, but rather to "point to" a value of a certain type. (Using the term "point to" here on a high-level, as in "redirect to".)

I think it doesn't matter whether the value is stored in the same memory as the smart pointer (as in case of Cow::Owned) or somewhere else on the heap (as in case of Box). I would thus conclude that Cow indeed can be considered to be a smart pointer (even in case of Cow::Owned); opposed to the newtype pattern, which supposes to create a new type rather than pointing to a value of a certain other type.

I suggest saying that exact phrase during a job interview. Oh, and I also suggest saying that pointer is just a memory address. You'll see if you get the job.

I would feel very comfortable using that exact same phrase in a job interview. If they called me out on it I could back it up. By showing this thread for example.

A pointer is a memory address. That is the whole idea of the thing.

Of course at the machine instruction level, in assembler, there is nothing to distinguish a pointer from an integer or whatever else. As long as it is big enough to hold a memory address you can use it as one.

Languages like C, C++, Pascal add some semantics around pointers. Like what type they point to. But still a pointer is physically just enough bits to hold a memory address.

Rust takes the semantics further, with lifetimes for starters.

Then so called "smart pointers" that add reference counts or whatever. Now you physically need the bits for the memory address and more bits for whatever else is required, like reference counts.

I would be very happy to discuss all this at an interview.

<aside>
Many years ago I started expounding at an interview on why C was perhaps not a wise choice for a large, real-time, safety-critical, project. What with the ease with which it possible to introduce hard to debug bugs with the use of pointers and manual memory management.

They were using C of course. I did not get the job. Looking back I'm glad about that.

Now a days, I'm the boss, I interview myself :slight_smile:
</aside>

Perhaps we should let this debate rest. I think we both know what we are talking about. We just use different words to talk about it.

Yes, you can. And you was the one who bringed up "that logic":

It seems like your suggestions contradict with your definitions.

Maybe a new thread would be good? This has diverged off the original topic (which already has an accepted solution, too)

1 Like

:+1:

Done: What are smart pointers?

1 Like