diff --git a/annual-survey.md b/annual-survey.md
index b88f236..33fd236 100644
--- a/annual-survey.md
+++ b/annual-survey.md
@@ -41,7 +41,7 @@ it should nevertheless be helpful as a guide.
1. Analyze the results and publish a blog post
- There is a dedicated section about this [below](#analysing-survey-results)
2. Add a link to the previous survey announcement blog post pointing to the blog post with survey results
-3. Update [forge.rust-lang.org](https://forge.rust-lang.org/community/survey-faq.html?highlight=survey#where-can-i-see-the-previous-survey-reports) with a link to the results blog post.
+3. Update [forge.rust-lang.org](https://forge.rust-lang.org/community/survey-faq.html#where-can-i-see-the-previous-survey-reports) with a link to the results blog post.
4. Update [FAQ](documents/Community-Survey-FAQ.md) with a link to the results blog post.
### Analysing survey results
diff --git a/report/README.md b/report/README.md
index 1844b36..708bd53 100644
--- a/report/README.md
+++ b/report/README.md
@@ -3,16 +3,30 @@ This directory contains scripts that automate the generation of charts, reports
surveys created in SurveyHero.
Note that these scripts are intended to be used as a library, so you will need to write your own script to leverage them.
-It is best to take a look at their usage from previous surveys, and start with that.
+It is best to take a look at their usage from previous surveys, and start with that. The scripts will change over time, they are only kept compatible with the latest version of the annual survey. If you need to re-render the reports from an older survey, you should use an older version of these scripts from the corresponding year.
+
+# Build and install
+
+First install system dependencies, the development packages for `libxml2`, `libxslt1`, `zlib1g`, `libjpeg` and python3. For Debian is for example:
+``` bash
+sudo apt install libxml2-dev libxslt1-dev zlib1g-dev libjpeg-dev libpython3-dev
+```
+
+Ensure you have Python 3.8 installed (last minor release is 3.8.20). Specifically an old version of the `lxml` library is used and due to [this bug](https://bugs.launchpad.net/lxml/+bug/1973155) an accordingly old version of Python is required. If your distribution does not ship anymore with Python 3.8.x you'll have to compile it yourself ([instructions](https://stackoverflow.com/a/62831268)).
To use the scripts, you should install their dependencies first:
```bash
$ python3 -m venv venv
$ source venv/venv/bin/activate
-(venv) $ pip install -U setuptools wheel pip
+(venv) $ pip install -U setuptools wheel pip
(venv) $ pip install -r requirements.txt
```
+Also ensure to install the Pillow library (this step fixes a ValueError "WordCloud Only Supported for TrueType fonts")
+```bash
+(venv) $ pip install -U pillow
+```
+
and then add this directory to the `PYTHONPATH` of your main Python script, and then use e.g. `from surveyhero.parser import parse_surveyhero_report`.
## Useful functions
diff --git a/report/requirements.txt b/report/requirements.txt
index 9541163..add827d 100644
--- a/report/requirements.txt
+++ b/report/requirements.txt
@@ -1,7 +1,7 @@
plotly==5.18.0
kaleido==0.2.1
-pandas==2.1.4
-matplotlib==3.8.2
+pandas==2.0.3
+matplotlib==3.7.5
wordcloud==1.9.3
elsie[cairo]==3.4
beautifulsoup4==4.12.3
diff --git a/report/surveyhero/chart.py b/report/surveyhero/chart.py
index 6de401d..f3ed7dc 100644
--- a/report/surveyhero/chart.py
+++ b/report/surveyhero/chart.py
@@ -22,9 +22,11 @@ def format_title(question: Question, include_kind: bool = False) -> str:
return f'{wrap_text(question.question, max_width=75)}
(total responses = {question.total_responses}{kind})'
-def wrap_text(text: str, max_width: int) -> str:
- text = textwrap.wrap(text, width=max_width, break_long_words=False)
- text = "
".join(text)
+def wrap_text(text: str, max_width: int, override_line_size: Optional[str] = None) -> str:
+ lines = textwrap.wrap(text, width=max_width, break_long_words=False)
+ if override_line_size is not None:
+ lines = [f"{line}" for line in lines]
+ text = "
".join(lines)
return text
@@ -35,13 +37,32 @@ def make_bar_chart(
xaxis_tickangle=0,
max_tick_width=30,
legend_order: Optional[List[str]] = None,
- layout_args: Optional[Dict[str, Any]] = None
+ layout_args: Optional[Dict[str, Any]] = None,
+ legend_params: Optional[Dict[str, Any]] = None,
) -> Figure:
assert len(questions) > 0
assert len(set(question.year for question in questions)) == len(questions)
+ # Sort questions by year to have a left-to-right reading order
+ questions = sorted(questions, key=lambda q: q.year)
+
+ # Plotly hardcodes the line height to be 1.3em, which is quite large, and it makes it
+ # hard to visually parse different lines vs different X axis ticks.
+ # Therefore, we use a hack - we set the xaxis font size to be 9 instead of 12
+ # (the defaut font size), to reduce the line height proportionally (9 * 1.3 instead of
+ # 12 * 1.3).
+ # And then we inflate the font size of the individual lines by 12/9 to make the actual
+ # font size be the same as before applying the hack.
+ xaxis_font_size = 9
+ override_line_size = f"{12 / xaxis_font_size:.1f}em"
+
if legend_order is not None:
- legend_order = [wrap_text(l, max_width=max_tick_width) for l in legend_order]
+ # We need to apply the size hack also to the legend, otherwise the answers won't match
+ legend_order = [wrap_text(
+ l,
+ max_width=max_tick_width,
+ override_line_size=override_line_size
+ ) for l in legend_order]
data = defaultdict(list)
totals = {}
@@ -49,7 +70,11 @@ def make_bar_chart(
for question in questions:
assert question.is_simple()
for answer in question.kind.answers:
- text = wrap_text(answer.answer, max_width=max_tick_width)
+ text = wrap_text(
+ answer.answer,
+ max_width=max_tick_width,
+ override_line_size=override_line_size
+ )
data["year"].append(str(question.year))
data["answer"].append(text)
@@ -80,7 +105,7 @@ def make_bar_chart(
counts = data.loc[data["Year"] == year, "count"].astype(np.float32)
data.loc[data["Year"] == year, "percent"] = (counts / total_count) * 100.0
- main_year = str(questions[0].year)
+ main_year = str(questions[-1].year)
def sort_key(answer: str) -> int:
if legend_order is not None:
@@ -100,11 +125,18 @@ def generate_text(row) -> str:
data["text"] = data.apply(generate_text, axis=1)
+ palette = px.colors.qualitative.Plotly
+ # Make sure that we have a canonical assignment of colors to individual years
+ # If there is only a single year, we should assign it palette[0]
+ # If there are two years, the largest one should have palette[0], the other one palette[1] etc.
+ palette = palette[:len(questions)][::-1]
+
fig = px.bar(
data,
x="answer",
y="percent",
color="Year",
+ color_discrete_sequence=palette,
barmode="group",
text="text",
custom_data=["Year", "count"],
@@ -114,9 +146,13 @@ def generate_text(row) -> str:
fig.update_traces(
textposition="outside",
hovertemplate="Year: %{customdata[0]}
Count: %{customdata[1]}
Percent: %{text}",
- textangle=-90 if bar_label_vertical else 0,
+ textangle=90 if bar_label_vertical else 0,
)
+ legend = {}
+ if legend_params is not None:
+ legend.update(legend_params)
+
layout_args = layout_args or {}
fig.update_layout(
meta="bar-chart",
@@ -129,6 +165,8 @@ def generate_text(row) -> str:
xaxis_title=None,
# xaxis_tickwidth=40,
xaxis_tickangle=xaxis_tickangle,
+ # See usage of `override_line_size` above
+ xaxis_tickfont=dict(size=xaxis_font_size),
yaxis_title="Percent out of all responses (%)",
yaxis_range=[0, 119],
yaxis_ticksuffix="%",
@@ -144,6 +182,7 @@ def generate_text(row) -> str:
pad=10,
b=10
),
+ legend=legend,
dragmode="pan",
**layout_args
)
@@ -239,10 +278,12 @@ def make_matrix_chart(
question: Question,
categories: List[str],
category_label: str,
- height=600,
+ option_label: Optional[str] = None,
+ height: Optional[int] = None,
horizontal: bool = False,
max_label_width=20,
- legend_params: Optional[Dict[str, Any]] = None
+ legend_params: Optional[Dict[str, Any]] = None,
+ textposition = "outside"
) -> Figure:
"""
Create a matrix chart with different categories.
@@ -278,6 +319,12 @@ def make_matrix_chart(
if not horizontal:
keys = dict(y="Count", x="Category")
+ if height is None:
+ if horizontal:
+ height = 600
+ else:
+ height = 1000
+
fig = px.bar(
df,
**keys,
@@ -287,12 +334,12 @@ def make_matrix_chart(
Category=group_keys
),
title=format_title(question),
- height=1000 if not horizontal else height,
+ height=height,
hover_data=[category_label]
)
fig.update_traces(
orientation="h" if horizontal else "v",
- textposition="outside",
+ textposition=textposition,
hovertemplate=f"Category: %{{y}}
{category_label}: %{{customdata[0]}}
Percent: %{{text}}",
)
@@ -302,7 +349,18 @@ def make_matrix_chart(
layout_args = {}
if horizontal:
- layout_args["xaxis_range"] = [0, 110]
+ if textposition != "inside":
+ layout_args["xaxis_range"] = [0, 110]
+ else:
+ layout_args["xaxis_range"] = [0, 100]
+ layout_args["xaxis_title"] = None
+ layout_args["xaxis_ticksuffix"] = "%"
+ layout_args["yaxis_ticksuffix"] = ""
+ layout_args["yaxis_title"] = option_label
+ else:
+ layout_args["yaxis_title"] = None
+ layout_args["xaxis_title"] = option_label
+ layout_args["yaxis_ticksuffix"] = "%"
fig.update_layout(
meta="matrix-chart",
@@ -312,13 +370,9 @@ def make_matrix_chart(
font_family="Rockwell",
),
# hovermode="y unified",
- yaxis_title=None,
yaxis_tickangle=0,
# https://stackoverflow.com/a/52397461/1107768
- yaxis_ticksuffix=" ",
yaxis_fixedrange=True,
- xaxis_title="Percent out of the category (%)",
- xaxis_ticksuffix="%",
xaxis_fixedrange=True,
legend=legend,
dragmode="pan",
diff --git a/report/surveyhero/report.py b/report/surveyhero/report.py
index 3453e94..5fc40bf 100644
--- a/report/surveyhero/report.py
+++ b/report/surveyhero/report.py
@@ -48,10 +48,8 @@ class ChartReport:
def __init__(self):
self.charts: Dict[str, ChartRenderer] = {}
- def add_bar_chart(self, name: str, question: Question, baseline: Optional[Question] = None, **kwargs):
- questions = [question]
- if baseline is not None:
- questions.append(baseline)
+ def add_bar_chart(self, name: str, question: Question, *baselines: Question, **kwargs):
+ questions = [question] + list(baselines)
def render_fn(**args):
return make_bar_chart(questions=questions, **join(kwargs, args))
diff --git a/report/surveyhero/survey.py b/report/surveyhero/survey.py
index 02329cd..92b8475 100644
--- a/report/surveyhero/survey.py
+++ b/report/surveyhero/survey.py
@@ -25,7 +25,8 @@ def rename_answers(self, diff: Dict[str, Optional[str]]) -> "SimpleQuestion":
continue
answer = dataclasses.replace(answer, answer=updated)
answers.append(answer)
- assert len(diff) == 0
+ if len(diff) != 0:
+ raise Exception(f"Some diffs were not applied: {diff}\nAnswers: {self.answers}")
return dataclasses.replace(self, answers=answers)
@@ -42,7 +43,7 @@ def rename_answers(self, diff: Dict[str, str]) -> "MatrixQuestion":
group = diff.pop(group)
answer_groups[group] = items
if len(diff) > 0:
- raise Exception(f"Rename answers diff not empty: {diff}")
+ raise Exception(f"Rename answers diff not empty: {diff}. Answers: {self.answer_groups}")
return dataclasses.replace(self, answer_groups=answer_groups)
@@ -77,6 +78,8 @@ def combine_answers(self, diff: Dict[str, List[str]]) -> "Question":
for (target, old_answers) in diff.items():
count = 0
for answer in old_answers:
+ if answer not in answers_orig:
+ raise Exception(f"Answer {answer} not in {answers_orig}")
count += answers_orig[answer].count
answers_orig.pop(answer)
assert count > 0
diff --git a/surveys/2024-annual-survey/report/2025-02-13-2024-State-Of-Rust-Survey-results.md b/surveys/2024-annual-survey/report/2025-02-13-2024-State-Of-Rust-Survey-results.md
new file mode 100644
index 0000000..2aee3f7
--- /dev/null
+++ b/surveys/2024-annual-survey/report/2025-02-13-2024-State-Of-Rust-Survey-results.md
@@ -0,0 +1,174 @@
+---
+layout: post
+title: "2024 State of Rust Survey Results"
+author: The Rust Survey Team
+---
+
+Hello, Rustaceans!
+
+The Rust Survey Team is excited to share the results of our [2024 survey on the Rust Programming language](https://blog.rust-lang.org/2024/12/05/annual-survey-2024-launch.html), conducted between December 5, 2024 and December 23, 2024.
+As in previous years, the 2024 State of Rust Survey was focused on gathering insights and feedback from Rust users, and all those who are interested in the future of Rust more generally.
+
+This ninth edition of the survey surfaced new insights and learning opportunities straight from the global Rust language community, which we will summarize below. In addition to this blog post, **we have also prepared a [report][report]** containing charts with aggregated results of all questions in the survey.
+
+**Our sincerest thanks to every community member who took the time to express their opinions and experiences with Rust over the past year. Your participation will help us make Rust better for everyone.**
+
+There's a lot of data to go through, so strap in and enjoy!
+
+## Participation
+
+| **Survey** | **Started** | **Completed** | **Completion rate** | **Views** |
+|:----------:|------------:|--------------:|--------------------:|----------:|
+| 2023 | 11 950 | 9 710 | 82.2% | 16 028 |
+| 2024 | 9 450 | 7 310 | 77.4% | 13 564 |
+
+As shown above, in 2024, we have received fewer survey views than in the previous year. This was likely caused simply by the fact that the survey ran only for two weeks, while in the previous year it ran for almost a month. However, the completion rate has also dropped, which seems to suggest that the survey might be a bit too long. We will take this into consideration for the next edition of the survey.
+
+## Community
+
+The State of Rust survey not only gives us excellent insight into how many Rust users around the world are using and experiencing the language but also gives us insight into the makeup of our global community. This information gives us a sense of where the language is being used and where access gaps might exist for us to address over time. We hope that this data and our related analysis help further important discussions about how we can continue to prioritize global access and inclusivity in the Rust community.
+
+Same as every year, we asked our respondents in which country they live in. The top 10 countries represented were, in order: United States (22%), Germany (14%), United Kingdom (6%), France (6%), China (5%), Canada (3%), Netherlands (3%), Russia (3%), Australia (2%), and Sweden (2%). We are happy to see that Rust is enjoyed by users from all around the world! You can try to find your country in the chart below:
+
+
+
+We also asked whether respondents consider themselves members of a marginalized community. Out of those who answered, 74.5% selected no, 15.5% selected yes, and 10% preferred not to say.
+
+We have asked the group that selected “yes” which specific groups they identified as being a member of. The majority of those who consider themselves a member of an underrepresented or marginalized group in technology identify as lesbian, gay, bisexual, or otherwise non-heterosexual. The second most selected option was neurodivergent at 46% followed by trans at 35%.
+
+
+
+Each year, we must acknowledge the diversity, equity, and inclusivity (DEI) related gaps in the Rust community and open source as a whole. We believe that excellent work is underway at the Rust Foundation to advance global access to Rust community gatherings and distribute grants to a diverse pool of maintainers each cycle, which you can learn more about [here](https://rustfoundation.org/community). Even so, global inclusion and access is just one element of DEI, and the survey working group will continue to advocate for progress in this domain.
+
+## Rust usage
+
+The number of respondents that self-identify as a Rust user was quite similar to last year, around 92%. This high number is not surprising, since we primarily target existing Rust developers with this survey.
+
+
+
+Similarly as last year, around 31% of those who did not identify as Rust users cited the perception of difficulty as the primary reason for not using Rust. The most common reason for not using Rust was that the respondents simply haven’t had the chance to try it yet.
+
+
+
+Of the former Rust users who participated in the 2024 survey, 36% cited factors outside their control as a reason why they no longer use Rust, which is a 10pp decrease from last year. This year, we also asked respondents if they would consider using Rust again if an opportunity comes up, which turns out to be true for a large fraction of the respondents (63%). That is good to hear!
+
+
+
+> Closed answers marked with N/A were not present in the previous version(s) of the survey.
+
+Those not using Rust anymore told us that it is because they don't really need it (or the goals of their company changed) or because (like above) it was not the right tool for the job. A few reported being overwhelmed by the language or its ecosystem in general or that switching or introducing Rust would have been too expensive in terms of human effort.
+
+Of those who used Rust in 2024, 53% did so on a daily (or nearly daily) basis — an increase of 4pp from the previous year. We can observe an upward trend in the frequency of Rust usage over the past few years, which suggests that Rust is being increasingly used at work. This is also confirmed by other answers mentioned in the Rust at Work section later below.
+
+
+
+Rust expertise is also continually increasing amongst our respondents! 20% of respondents can write (only) simple programs in Rust (a decrease of 3pp from 2023), while 53% consider themselves productive using Rust — up from 47% in 2023. While the survey is just one tool to measure the changes in Rust expertise overall, these numbers are heartening as they represent knowledge growth for many Rustaceans returning to the survey year over year.
+
+
+
+Unsurprisingly, the most popular version of Rust is *latest stable*, either the most recent one or whichever comes with the users' Linux distribution. Almost a third of users also use the latest nightly release, due to various reasons (see below). However, it seems that the beta toolchain is not used much, which is a bit unfortunate. We would like to encourage Rust users to use the beta toolchain more (e.g. in CI environments) to help test soon-to-be stabilized versions of Rust.
+
+
+
+People that use the nightly toolchain mostly do it to gain access to specific unstable language features. Several users have also mentioned that rustfmt works better for them on nightly or that they use the nightly compiler because of faster compilation times.
+
+
+
+## Learning Rust
+To use Rust, programmers first have to learn it, so we are always interested in finding out how do they approach that. Based on the survey results, it seems that most users learn from Rust documentation and also from [The Rust Programming Language](https://doc.rust-lang.org/book/) book, which has been a favourite learning resource of new Rustaceans for a long time. Many people also seem to learn by reading the source code of Rust crates. The fact that both the documentation and source code of tens of thousands of Rust crates is available on [docs.rs](https://docs.rs) and GitHub makes this easier.
+
+
+
+In terms of answers belonging to the "Other" category, they can be clustered into three categories: people using LLM (large language model) assistants (Copilot, ChatGPT, Claude, etc.), reading the official Rust forums (Discord, [URLO][urlo]) or being mentored while contributing to Rust projects. We would like to extend a big thank you to those making our spaces friendly and welcoming for newcomers, as it is important work and it pays off. Interestingly, a non-trivial number of people "learned by doing" and used rustc error messages and clippy as a guide, which is a good indicator of the quality of Rust diagnostics.
+
+In terms of formal education, it seems that Rust has not yet penetrated university curriculums, as this is typically a very slowly moving area. Only a very small number of respondents (around 3%) have taken a university Rust course or use university learning materials.
+
+
+
+[urlo]: https://users.rust-lang.org/
+
+## Programming environment
+
+In terms of operating systems used by Rustaceans, Linux was the most popular choice, and it seems that it is getting increasingly popular year after year. It is followed by macOS and Windows, which have a very similar share of usage.
+
+
+
+> As you can see in the [wordcloud](../../../images/2025-02-13-rust-survey-2024/which-os-do-you-use-wordcloud.png), there are also a few users that prefer Arch, btw.
+
+Rust programmers target a diverse set of platforms with their Rust programs. We saw a slight uptick in users targeting embedded and mobile platforms, but otherwise the distribution of platforms stayed mostly the same as last year. Since the WebAssembly target is quite diverse, we have split it into two separate categories this time. Based on the results it is clear that when using WebAssembly, it is mostly in the context of browsers (23%) rather than other use-cases (7%).
+
+
+
+We cannot of course forget the favourite topic of many programmers: which IDE (developer environment) they use. Although Visual Studio Code still remains the most popular option, its share has dropped by 5pp this year. On the other hand, the Zed editor seems to have gained considerable traction recently. The small percentage of those who selected "Other" are using a wide range of different tools: from CursorAI to classics like Kate or Notepad++. Special mention to the 3 people using "ed", that's quite an achievement.
+
+
+
+> You can also take a look at the linked [wordcloud](../../../images/2025-01-rust-survey-2024/what-ide-do-you-use-wordcloud.png) that summarizes open answers to this question (the "Other" category), to see what other editors are also popular.
+
+## Rust at Work
+
+We were excited to see that more and more people use Rust at work for the majority of their coding, 38% vs 34% from last year. There is a clear upward trend in this metric over the past few years.
+
+
+
+The usage of Rust within companies also seems to be rising, as 45% of respondents answered that their organisation makes non-trivial use of Rust, which is a 7pp increase from 2023.
+
+
+
+Once again, the top reason employers of our survey respondents invested in Rust was the ability to build relatively correct and bug-free software. The second most popular reason was Rust’s performance characteristics. 21% of respondents that use Rust at work do so because they already know it, and it's thus their default choice, an uptick of 5pp from 2023. This seems to suggest that Rust is becoming one of the baseline languages of choice for more and more companies.
+
+
+
+Similarly to the previous year, a large percentage of respondents (82%) report that Rust helped their company achieve its goals. In general, it seems that programmers and companies are quite happy with their usage of Rust, which is great!
+
+
+
+In terms of technology domains, the situation is quite similar to the previous year. Rust seems to be especially popular for creating server backends, web and networking services and cloud technologies. It also seems to be gaining more traction for embedded use-cases.
+
+
+
+> You can scroll the chart to the right to see more domains. Note that the Automotive domain was not offered as a closed answer in the 2023 survey (it was merely entered through open answers), which might explain the large jump.
+
+It is exciting to see the continued growth of professional Rust usage and the confidence so many users feel in its performance, control, security and safety, enjoyability, and more!
+
+## Challenges
+
+As always, one of the main goals of the State of Rust survey is to shed light on challenges, concerns, and priorities on Rustaceans’ minds over the past year.
+
+We have asked our users about aspects of Rust that limit their productivity. Perhaps unsurprisingly, slow compilation was at the top of the list, as it seems to be a perennial concern of Rust users. As always, there are efforts underway to improve the speed of the compiler, such as enabling the [parallel frontend](https://blog.rust-lang.org/2023/11/09/parallel-rustc.html) or switching to a [faster linker by default](https://blog.rust-lang.org/2024/05/17/enabling-rust-lld-on-linux.html). We invite you to test these improvements and let us know if you encounter any issues.
+
+Other challenges included subpar support for debugging Rust and high disk usage of Rust compiler artifacts. On the other hand, most Rust users seem to be very happy with its runtime performance, the correctness and stability of the compiler and also Rust's documentation.
+
+
+
+In terms of specific unstable (or missing) features that Rust users want to be stabilized (or implemented), the most desired ones were async closures and if/let while chains. Well, we have good news! Async closures will be stabilized in the next version of Rust (1.85), and if/let while chains will hopefully follow [soon after](https://github.com/rust-lang/rust/pull/132833), once Edition 2024 is released (which will also happen in Rust 1.85).
+
+Other coveted features are generators (both sync and async) and more powerful generic const expressions. You can follow the [Rust Project Goals](https://rust-lang.github.io/rust-project-goals/2025h1/goals.html) to track the progress of these (and other) features.
+
+People were really helpful and tried hard pointing their most notable issues limiting productivity in the open answers to this question. We have seen mentions of struggles with async programming (an all-time favourite), debuggability of errors (which people generally love, but they are not perfect for everyone) or Rust tooling being slow or resource intensive (rust-analyzer and rustfmt). Some users also want a better IDE story and improved interoperability with other languages.
+
+
+
+This year, we have also included a new question about the speed of Rust's evolution. While most people seem to be content with the status quo, more than a quarter of people who responded to this question would like Rust to stabilize and/or add features more quickly, and only 7% of respondents would prefer Rust to slow down or completely stop adding new features.
+
+
+
+Interestingly, when we asked respondents about their main worries for the future of Rust, one of the top answers remained the worry that Rust will become too complex. This seems to be in contrast with the answers to the previous question. Perhaps Rust users still seem to consider the complexity of Rust to be manageable, but they worry that one day it might become too much.
+
+We are happy to see that the amount of respondents concerned about Rust Project governance and lacking support of the Rust Foundation has dropped by about 6pp from 2023.
+
+
+
+## Looking ahead
+
+Each year, the results of the State of Rust survey help reveal the areas that need improvement in many areas across the Rust Project and ecosystem, as well as the aspects that are working well for our community.
+
+If you have any suggestions for the Rust Annual survey, please [let us know](https://github.com/rust-lang/surveys/issues)!
+
+We are immensely grateful to those who participated in the 2024 State of Rust Survey and facilitated its creation. While there are always challenges associated with developing and maintaining a programming language, this year we were pleased to see a high level of survey participation and candid feedback that will truly help us make Rust work better for everyone.
+
+If you’d like to dig into more details, we recommend you to browse through the full [survey report][report].
+
+[report]: https://raw.githubusercontent.com/rust-lang/surveys/main/surveys/2024-annual-survey/report/annual-survey-2024-report.pdf
+
+
diff --git a/surveys/2024-annual-survey/report/main.py b/surveys/2024-annual-survey/report/main.py
new file mode 100644
index 0000000..e0fb51a
--- /dev/null
+++ b/surveys/2024-annual-survey/report/main.py
@@ -0,0 +1,479 @@
+import random
+import sys
+from collections import defaultdict
+from pathlib import Path
+from typing import List
+
+import numpy as np
+
+ROOT_DIR = Path(__file__).absolute().parent.parent.parent.parent
+REPORT_SCRIPT_DIR = ROOT_DIR / "report"
+
+sys.path.insert(0, str(REPORT_SCRIPT_DIR))
+
+from surveyhero.parser import parse_surveyhero_report, parse_surveyhero_answers
+from surveyhero.render import render_blog_post, render_report_to_pdf
+from surveyhero.report import ChartReport
+from surveyhero.survey import Question, SurveyFullAnswers, SurveyReport, normalize_open_answers
+
+
+def print_answers(a: Question, b: Question):
+ assert a.is_simple()
+ assert b.is_simple()
+
+ a_answers = set(a.answer for a in a.kind.answers)
+ b_answers = set(a.answer for a in b.kind.answers)
+ answers = a_answers | b_answers
+ for answer in sorted(answers):
+ has_a = answer in a_answers
+ has_b = answer in b_answers
+ print(answer, has_a, has_b)
+
+
+def print_question_index(old: SurveyReport, new: SurveyReport, path: Path):
+ old_index = 0
+ new_index = 0
+
+ with open(path, "w") as f:
+ while old_index < len(old.questions) or new_index < len(new.questions):
+ if old_index < len(old.questions):
+ old_q = old.questions[old_index]
+ print(f"{old.year}/{old_index}: {old_q.question}", file=f)
+ old_index += 1
+ if new_index < len(new.questions):
+ new_q = new.questions[new_index]
+ print(f"{new.year}/{new_index}: {new_q.question}", file=f)
+ new_index += 1
+
+
+def print_answer_index(answers: SurveyFullAnswers, report: SurveyReport, path: Path):
+ with open(path, "w") as f:
+ for (index, question) in enumerate(answers.questions):
+ if any(question == q.question for q in report.questions) and index > 0:
+ print(file=f)
+ print(f"{index}: {question}", file=f)
+
+
+def inspect_open_answers(answers: List[str]):
+ normalized = defaultdict(int)
+ for answer in answers:
+ answer = answer.strip().lower()
+ normalized[answer] += 1
+ items = sorted(normalized.items(), key=lambda x: x[1], reverse=True)
+ for (value, count) in items:
+ print(f"{value}: {count}")
+
+
+def annual_survey_2024_report() -> ChartReport:
+ r_2022 = parse_surveyhero_report(Path(ROOT_DIR / "data/data-2022.csv"), year=2022)
+ r_2023 = parse_surveyhero_report(Path(ROOT_DIR / "data/data-2023.csv"), year=2023)
+ r_2024 = parse_surveyhero_report(Path(ROOT_DIR / "data/data-2024.csv"), year=2024)
+ answers_2023 = parse_surveyhero_answers(Path(ROOT_DIR / "data/data-full-2023.csv"), year=2023)
+ answers_2024 = parse_surveyhero_answers(Path(ROOT_DIR / "data/data-full-2024.csv"), year=2024)
+
+ print_question_index(r_2023, r_2024, Path("questions.txt"))
+ print_answer_index(answers_2024, r_2024, Path("answers.txt"))
+
+ agreement_categories = ["Agree", "Disagree"]
+ agreement_label = "Response"
+
+ frequency_categories = ["More frequently than weekly", "Weekly", "Monthly or less frequently", "Never"]
+ frequency_label = "Frequency"
+
+ report = ChartReport()
+
+ rename = {
+ "Yes, I use Rust (for any purpose, even if you're just learning)": "Yes, I use Rust",
+ }
+ report.add_bar_chart("do-you-use-rust", r_2024.q(0).rename_answers(rename), r_2023.q(0).rename_answers(rename))
+
+ base = r_2023.q(1).rename_answers({
+ "I no longer have the opportunity to use Rust due to factors outside of my control":
+ "I no longer have the opportunity to use Rust due to factors outside my control"
+ })
+ report.add_bar_chart("why-did-you-stop-using-rust", r_2024.q(1), base,
+ bar_label_vertical=True,
+ xaxis_tickangle=45,
+ max_tick_width=50)
+ report.add_wordcloud("why-did-you-stop-using-rust-wordcloud", answers_2024.answers[10] + answers_2024.answers[11])
+
+ base = r_2023.q(2).rename_answers({
+ "I can't use Rust due to factors outside of my control":
+ "I can't use Rust due to factors outside my control"
+ })
+ report.add_bar_chart("why-dont-you-use-rust", r_2024.q(2), base,
+ bar_label_vertical=True,
+ xaxis_tickangle=45,
+ max_tick_width=40)
+ report.add_wordcloud("why-dont-you-use-rust-wordcloud", answers_2024.answers[23])
+
+ report.add_bar_chart("how-often-do-you-use-rust", r_2024.q(3), r_2023.q(3), r_2022.q(3),
+ bar_label_vertical=True)
+
+ expertise_diff = {
+ "I can write useful, production-ready code but it is a struggle": "I can write useful, production-ready code, but it is a struggle"
+ }
+ report.add_bar_chart(
+ "how-would-you-rate-your-rust-expertise",
+ r_2024.q(4),
+ r_2023.q(5).rename_answers(expertise_diff),
+ r_2022.q(4).rename_answers({
+ **expertise_diff,
+ "I can't read or write Rust": "I can't write Rust code"
+ }),
+ bar_label_vertical=True,
+ xaxis_tickangle=45,
+ max_tick_width=40
+ )
+ report.add_pie_chart("when-did-you-learn-rust", r_2024.q(5))
+
+ # Let's not use base, as the answers are too different
+ _base = r_2023.q(23).rename_answers({
+ "Other (please specify)": "Other",
+ "Videos": "Videos or live-streams",
+ "Online exercises (Rustlings, Rust by Example, etc.)": "Online exercises (Rustlings, 100 Exercises To Learn Rust, etc.)"
+ })
+ report.add_bar_chart("what-kind-of-learning-materials-have-you-consumed", r_2024.q(6).rename_answers({
+ "Other (please specify)": "Other"
+ }), xaxis_tickangle=45, bar_label_vertical=True, max_tick_width=40)
+ report.add_wordcloud("what-kind-of-learning-materials-have-you-consumed-wordcloud", answers_2024.answers[37])
+
+ report.add_bar_chart("have-you-taken-a-rust-course", r_2024.q(7), r_2023.q(22))
+
+ windows_diff = {
+ "Windows": ["Windows 10/11", "Windows 8 or older"]
+ }
+ report.add_bar_chart("which-os-do-you-use",
+ r_2024.q(8).combine_answers(windows_diff),
+ r_2023.q(7).combine_answers(windows_diff),
+ r_2022.q(6).rename_answers({
+ "Mac OS": "macOS"
+ }),
+ bar_label_vertical=True,
+ max_tick_width=20)
+ report.add_wordcloud("which-os-do-you-use-wordcloud", answers_2024.answers[45])
+
+ report.add_bar_chart("which-os-do-you-target",
+ r_2024.q(9).combine_answers(windows_diff),
+ r_2023.q(8).combine_answers(windows_diff),
+ bar_label_vertical=True,
+ xaxis_tickangle=45,
+ max_tick_width=50)
+ report.add_wordcloud("which-os-do-you-target-wordcloud", answers_2024.answers[58])
+
+ ide_diff = {
+ "Rust Rover (dedicated IntelliJ Rust IDE)": "Rust Rover"
+ }
+ zed_2023 = normalize_open_answers(answers_2023.answers[83])
+ zed_2024 = normalize_open_answers(answers_2024.answers[70])
+ report.add_bar_chart("what-ide-do-you-use",
+ r_2024.q(10).rename_answers(ide_diff).add_open(zed_2024, "zed", "Zed"),
+ r_2023.q(12).rename_answers(ide_diff).add_open(zed_2023, "zed", "Zed"),
+ bar_label_vertical=True, xaxis_tickangle=45)
+ report.add_wordcloud("what-ide-do-you-use-wordcloud", answers_2024.answers[70])
+
+ report.add_bar_chart("which-version-of-rust-do-you-use", r_2024.q(11),
+ xaxis_tickangle=45,
+ max_tick_width=40)
+ report.add_wordcloud("which-version-of-rust-do-you-use-wordcloud", answers_2024.answers[81])
+
+ report.add_bar_chart("if-you-use-nightly-why", r_2024.q(12), xaxis_tickangle=45, max_tick_width=40)
+ report.add_wordcloud("if-you-use-nightly-why-wordcloud", answers_2024.answers[93])
+
+ report.add_pie_chart("what-is-the-oldest-version-of-rust-you-use", r_2024.q(13))
+
+ report.add_matrix_chart(
+ "what-do-you-think-about-rust-stability",
+ r_2024.q(14),
+ height=800,
+ categories=agreement_categories,
+ category_label=agreement_label,
+ option_label="Statement"
+ )
+
+ report.add_bar_chart(
+ "what-do-you-think-about-rust-evolution",
+ r_2024.q(15),
+ xaxis_tickangle=45,
+ )
+
+ categories = ["Would unblock my use-case", "Would improve my code", "Don't need it",
+ "Don't know what it is"]
+ report.add_matrix_chart(
+ "which-features-do-you-want-stabilized",
+ r_2024.q(16),
+ categories=categories,
+ category_label="Response",
+ horizontal=True,
+ height=800,
+ max_label_width=30,
+ textposition="inside",
+ legend_params=dict(
+ orientation="h",
+ y=-0.05,
+ )
+ )
+ report.add_wordcloud("which-features-do-you-want-stabilized-wordcloud", answers_2024.answers[121])
+
+ categories = ["Big problem for me", "Could be improved, but does not limit me", "Not an issue for me at all"]
+ problems_diff = {
+ "Subpar debugging experience (e.g. missing value visualizations or async stacktraces)": "Subpar debugging experience",
+ "Subpar IDE support (e.g. some errors are not shown or analysis is slow)": "Subpar IDE support",
+ "Encountering compiler bugs (e.g. ICEs a.k.a. internal compiler errors or miscompilations)": "Encountering compiler bugs"
+ }
+ report.add_matrix_chart(
+ "which-problems-limit-your-productivity",
+ r_2024.q(17).rename_answers(problems_diff),
+ categories=categories,
+ category_label="Response",
+ horizontal=True,
+ height=1000,
+ textposition="inside",
+ max_label_width=40,
+ legend_params=dict(
+ y=-0.05,
+ orientation="h"
+ )
+ )
+ report.add_wordcloud("which-problems-limit-your-productivity-wordcloud", answers_2024.answers[142])
+
+ categories = [
+ "I use this feature",
+ "I cannot use this feature yet",
+ "I do not need this feature",
+ "I did not know it was stabilized",
+ "I do not know what it is"
+ ]
+ report.add_matrix_chart(
+ "which-stabilized-features-do-you-use",
+ r_2024.q(18),
+ categories=categories,
+ category_label="Response",
+ horizontal=True,
+ height=1000,
+ textposition="inside",
+ max_label_width=40,
+ legend_params=dict(
+ y=-0.05,
+ orientation="h"
+ )
+ )
+
+ cargo_diff = {
+ "I combine Cargo and another build system": [
+ "I combine Cargo and another build system",
+ "If you use Cargo with (or just use) other build systems, which ones do you use?"
+ ]
+ }
+ report.add_bar_chart("how-do-you-build-your-rust-projects",
+ r_2024.q(19).combine_answers(cargo_diff),
+ r_2023.q(17).combine_answers(cargo_diff))
+ report.add_wordcloud("how-do-you-build-your-rust-projects-wordcloud", answers_2024.answers[162])
+
+ report.add_bar_chart("how-do-download-crates", r_2024.q(20), r_2023.q(18), xaxis_tickangle=45)
+
+ question = r_2024.q(21).rename_answers({
+ "Attend a Rust meetup or conference (virtual or in-person)": "Attend a Rust meetup",
+ "Consume informational content about Rust (e.g., blogs, live streams, YouTube videos, etc.)": "Consume content (blogs, videos, ...)",
+ "Contribute code changes (including tests) to any open-source Rust project": "Contribute code/tests to any OSS Rust project",
+ "Contribute non-code changes (documentation, comments, etc.) to any open-source Rust project": "Contribute docs/comments to any OSS Rust project",
+ "Discuss the Rust project in an official chat or forum (internals.rust-lang.org, Rust Zulip, etc.)": "Discuss Rust in official chat/forum",
+ "Open an issue on any repo in the rust-lang GitHub organization": "Open issue on any rust-lang repo",
+ "Participate in conversations about Rust on social media or websites (Hacker News, r/rust, Twitter, LinkedIn, etc.)": "Discuss Rust on social media (Hacker News, r/rust, ...)",
+ "Produce informational content about Rust (e.g., blogged, live streamed, made a YouTube video, presented at a conference/meetup, etc.)": "Produce Rust content (blog, video, talk, ...)",
+ "Read official Rust communication channels (e.g., This Week in Rust, the official Rust blog, the Rust Twitter account, etc.)": "Read official Rust channels (Rust blog/twitter, This Week in Rust, ...)",
+ "Write, comment on, contribute to discussion of, or provide edits to an open RFC": "Comment or contribute to an open RFC"
+ })
+ report.add_matrix_chart(
+ "how-often-do-you-engage-in-community-events",
+ question,
+ categories=frequency_categories,
+ category_label=frequency_label,
+ horizontal=True,
+ textposition="inside",
+ max_label_width=30,
+ legend_params=dict(
+ orientation="h"
+ ),
+ height=800
+ )
+
+ categories = [
+ "I feel welcome",
+ "I do not feel particularly welcome or unwelcome",
+ "I feel unwelcome",
+ "I've never participated in this activity"
+ ]
+ experience_diff = {
+ "Community focused on a specific area of Rust software development (e.g. game development, audio, etc.)": "Community focused on a specific Rust area",
+ "Discussions (issues, pull requests, etc.) on a repository inside the rust-lang GitHub organization": "Discussions on a rust-lang repo",
+ "Discussions (issues, pull requests, etc.) on a repository outside the rust-lang GitHub organization": "Discussions on a repo outside rust-lang",
+ "Official Rust community forums or chats (users.rust-lang.org, internals.rust-lang.org, the official Rust Discord, or the Rust Zulip)": "Official Rust forum/chat (URLO, IRLO, Discord, Zulip)",
+ "Unofficial Rust community forums or chats (e.g., reddit.com/r/rust, Hacker News, the Rust Community Discord, etc.)": "Unofficial Rust forum/chat (r/rust, Hacker News, ...)"
+ }
+ report.add_matrix_chart(
+ "what-was-your-experience-in-the-community",
+ r_2024.q(22).rename_answers(experience_diff),
+ categories=categories,
+ category_label="Response",
+ horizontal=True,
+ textposition="inside",
+ max_label_width=30,
+ legend_params=dict(
+ orientation="h"
+ )
+ )
+
+ report.add_pie_chart("are-you-employed", r_2024.q(23))
+ report.add_pie_chart("do-you-design-software", r_2024.q(24))
+
+ report.add_bar_chart("do-you-personally-use-rust-at-work", r_2024.q(25), r_2023.q(26),
+ r_2022.q(17),
+ max_tick_width=24)
+ report.add_bar_chart(
+ "how-is-rust-used-at-your-organization",
+ r_2024.q(26),
+ r_2023.q(27),
+ r_2022.q(20).rename_answers({
+ "I am unsure whether my company has considered using or currently uses Rust": "I am unsure whether my organisation has considered using or currently uses Rust",
+ "I don't work for a company or my company does not develop software of any kind": "I don't work for a organisation or my organisation does not develop software of any kind",
+ "My company has experimented with Rust or is considering using it": "My organisation has experimented with Rust or is considering using it",
+ "My company has not seriously considered Rust for any use": "My organisation has not seriously considered Rust for any use",
+ "My company makes non-trivial use of Rust (e.g., used in production or in significant tooling)": "My organisation makes non-trivial use of Rust (e.g., used in production or in significant tooling)"
+ }),
+ xaxis_tickangle=45,
+ bar_label_vertical=True
+ )
+ report.add_bar_chart(
+ "which-statements-apply-to-rust-at-work",
+ r_2024.q(27),
+ r_2023.q(28),
+ r_2022.q(19),
+ xaxis_tickangle=45,
+ bar_label_vertical=True
+ )
+
+ report.add_bar_chart(
+ "why-you-use-rust-at-work",
+ r_2024.q(28),
+ r_2023.q(29),
+ r_2022.q(18),
+ xaxis_tickangle=45,
+ bar_label_vertical=True
+ )
+
+ base_open = normalize_open_answers(answers_2023.answers[213], replace_spaces=True)
+ technology_diff = {
+ "Programming languages and related tools (including compilers, IDEs, standard libraries, etc.)": "Programming languages and related tools"
+ }
+ base = (
+ r_2023.q(30)
+ .rename_answers({
+ **technology_diff,
+ "Computer Games": "Computer games",
+ "Scientific and/or numeric computing": "Scientific and/or numerical computing",
+ })
+ .add_open(base_open, "automotive", "Automotive")
+ )
+
+ report.add_bar_chart("technology-domain", r_2024.q(29).rename_answers(technology_diff), base,
+ legend_params=dict(
+ orientation="h",
+ y=1
+ ),
+ xaxis_tickangle=90,
+ bar_label_vertical=True,
+ max_tick_width=50)
+ report.add_wordcloud("technology-domain-wordcloud", answers_2024.answers[235])
+
+ report.add_pie_chart("how-many-developers-does-your-organization-employ", r_2024.q(30),
+ legend_order=[
+ "Under 10",
+ "11-49",
+ "50-99",
+ "100-500",
+ "500-1,000",
+ "1,000-10,000",
+ "Over 10,000",
+ ])
+
+ report.add_bar_chart("is-your-organization-planning-on-hiring-rust-developers", r_2024.q(31), r_2023.q(32),
+ legend_order=[
+ "No",
+ "No (it is planning to hire other developers)",
+ "No (it is not planning to hire any developers)",
+ "Yes",
+ "I don't know",
+ ], xaxis_tickangle=45)
+
+ categories = [
+ "Agree",
+ "Neither agree nor disagree",
+ "Disagree"
+ ]
+ report.add_matrix_chart(
+ "assessment-of-rust-employment-statements",
+ r_2024.q(32),
+ categories=categories,
+ category_label="Response",
+ height=800
+ )
+
+ report.add_bar_chart("which-of-the-following-statements-about-rust-do-you-agree-with", r_2024.q(33), r_2023.q(33),
+ xaxis_tickangle=45, bar_label_vertical=True, max_tick_width=45)
+
+ worry_diff = {
+ "Tools and documentation are not accessible enough (e.g., due to language or incompatibility with screen readers)": "Tools and documentation are not accessible enough",
+ "Rust Foundation not supporting the Rust project properly (e.g. in financial, infrastructure, legal aspects)": "Rust Foundation not supporting the Rust project properly"
+ }
+ question = r_2024.q(34).rename_answers(worry_diff)
+ base = r_2023.q(34).rename_answers(worry_diff)
+ report.add_bar_chart("what-are-your-biggest-worries-about-rust", question, base, xaxis_tickangle=55,
+ max_tick_width=45, bar_label_vertical=True)
+ report.add_wordcloud("what-are-your-biggest-worries-about-rust-wordcloud", answers_2024.answers[264])
+
+ question = r_2024.q(35)
+ report.add_pie_chart("do-you-consider-yourself-to-be-a-part-of-an-underrepresented-group", question)
+
+ report.add_bar_chart("which-marginalized-group", r_2024.q(36), xaxis_tickangle=45)
+
+ report.add_bar_chart("are-you-a-student", r_2024.q(37), r_2023.q(38),
+ xaxis_tickangle=45,
+ max_tick_width=40,
+ bar_label_vertical=True)
+ report.add_pie_chart("how-long-have-you-been-programming", r_2024.q(38))
+ report.add_pie_chart("where-do-you-live", r_2024.q(39))
+ report.add_bar_chart("in-what-ways-are-you-comfortable-communicating", r_2024.q(40),
+ xaxis_tickangle=45, max_tick_width=45)
+ report.add_bar_chart("what-are-your-preferred-languages-for-technical-communication", r_2024.q(41),
+ xaxis_tickangle=45)
+ return report
+
+
+if __name__ == "__main__":
+ """
+ Renders the PDF report and a blog post for the 2024 Annual survey report.
+ Expects CSV data extracted from SurveyHero at .
+ See `annual_survey_2024_report` for more details.
+ """
+ random.seed(42)
+ np.random.seed(42)
+
+ report = annual_survey_2024_report()
+
+ render_report_to_pdf(
+ report,
+ Path(__file__).parent / "annual-survey-2024-report.pdf",
+ "Rust Annual survey 2024 report",
+ include_labels=False
+ )
+
+ resource_dir = "2025-02-13-rust-survey-2024"
+ # Fill path to blog root (i.e. a checkout of https://github.com/rust-lang/blog.rust-lang.org)
+ blog_root = Path("/projects/personal/rust/blog.rust-lang.org")
+ render_blog_post(
+ template=Path("2025-02-13-2024-State-Of-Rust-Survey-results.md"),
+ blog_root=blog_root,
+ resource_dir=resource_dir,
+ report=report
+ )